{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Convolutional Autoencoder on MNIST dataset\n",
    "\n",
    "**Learning Objective**\n",
    "1. Build an autoencoder architecture (consisting of an encoder and decoder) in Keras\n",
    "2. Define the loss using the reconstructive error\n",
    "3. Define a training step for the autoencoder using tf.GradientTape()\n",
    "4. Train the autoencoder on the MNIST dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction\n",
    "This notebook demonstrates how to build and train a convolutional autoencoder.\n",
    "\n",
    "Autoencoders consist of two models: an encoder and a decoder.\n",
    "\n",
    "<img src=\"../assets/autoencoder2.png\" width=\"600\">\n",
    "\n",
    "In this notebook we'll build an autoencoder to recreate MNIST digits. This notebook demonstrates this process on the MNIST dataset. The following animation shows a series of images produced by the *generator* as it was trained for 100 epochs. The images increasingly resemble hand written digits as the autoencoder learns to reconstruct the original images.\n",
    "\n",
    "<img src=\"../assets/autoencoder.gif\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Import TensorFlow and other libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import absolute_import, division, print_function\n",
    "\n",
    "import glob\n",
    "import imageio\n",
    "import os\n",
    "import PIL\n",
    "import time\n",
    "\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import tensorflow as tf\n",
    "\n",
    "from tensorflow.keras import layers\n",
    "from IPython import display"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we'll define some of the environment variables we'll use in this notebook. Note that we are setting the `EMBED_DIM` to be 64. This is the dimension of the latent space for our autoencoder. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(1)\n",
    "tf.random.set_seed(1)\n",
    "BATCH_SIZE = 128\n",
    "BUFFER_SIZE = 60000\n",
    "EPOCHS = 60\n",
    "LR = 1e-2\n",
    "EMBED_DIM = 64 # intermediate_dim"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load and prepare the dataset\n",
    "\n",
    "For this notebook, we will use the MNIST dataset to train the autoencoder. The encoder will map the handwritten digits into the latent space, to force a lower dimensional representation and the decoder will then map the encoding back."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "(train_images, _), (test_images, _) = tf.keras.datasets.mnist.load_data()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')\n",
    "train_images = (train_images - 127.5) / 127.5 # Normalize the images to [-1, 1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we define our input pipeline using `tf.data`. The pipeline below reads in `train_images` as tensor slices and then shuffles and batches the examples for training."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Batch and shuffle the data\n",
    "train_dataset = tf.data.Dataset.from_tensor_slices(train_images)\n",
    "train_dataset = train_dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE)\n",
    "train_dataset = train_dataset.prefetch(BATCH_SIZE*4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_images = test_images.reshape(test_images.shape[0], 28, 28, 1).astype('float32')\n",
    "test_images = (test_images - 127.5) / 127.5 # Normalize the images to [-1, 1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create the encoder and decoder models\n",
    "\n",
    "Both our encoder and decoder models will be defined using the [Keras Sequential API](https://www.tensorflow.org/guide/keras#sequential_model)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The Encoder\n",
    "\n",
    "The encoder uses [`tf.keras.layers.Conv2D`](https://www.tensorflow.org/api_docs/python/tf/keras/layers/Conv2D) layers to map the image into a lower-dimensional latent space. We will start with an image of size 28x28x1 and then use convolution layers to map into a final `Dense` layer."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise.** Complete the code below to create the CNN-based encoder model. Your model should have `input_shape` to be 28x28x1 and end with a final `Dense` layer the size of `embed_dim`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "#TODO 1.\n",
    "def make_encoder(embed_dim):\n",
    "    model = tf.keras.Sequential(name=\"encoder\")\n",
    "\n",
    "    # TODO: Your code goes here.\n",
    "    \n",
    "    assert model.output_shape == (None, embed_dim)\n",
    "    \n",
    "    return model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The Decoder\n",
    "\n",
    "The decoder uses [`tf.keras.layers.Conv2DTranspose`](https://www.tensorflow.org/api_docs/python/tf/keras/layers/Conv2DTranspose) (upsampling) layers to produce an image from the latent space. We will start with a `Dense` layer with the same input shape as `embed_dim`, then upsample several times until you reach the desired image size of 28x28x1."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise.** Complete the code below to create the decoder model. Start with a `Dense` layer that takes as input a tensor of size `embed_dim`. Use `tf.keras.layers.Conv2DTranspose` over multiple layers to upsample so that the final layer has shape 28x28x1 (the shape of our original MNIST digits).\n",
    "\n",
    "Hint: Experiment with using `BatchNormalization` or different activation functions like `LeakyReLU`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "#TODO 1.\n",
    "def make_decoder(embed_dim):\n",
    "    model = tf.keras.Sequential(name=\"decoder\")\n",
    "\n",
    "    # TODO: Your code goes here.\n",
    "    \n",
    "    assert model.output_shape == (None, 28, 28, 1)\n",
    "    \n",
    "    return model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we stitch the encoder and decoder models together to create our autoencoder. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "ae_model = tf.keras.models.Sequential([make_encoder(EMBED_DIM), make_decoder(EMBED_DIM)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using `.summary()` we can have a high-level summary of the full autoencoder model as well as the individual encoder and decoder. Note how the shapes of the tensors mirror each other as data is passed through the encoder and then the decoder."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"sequential\"\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "encoder (Sequential)         (None, 64)                608064    \n",
      "_________________________________________________________________\n",
      "decoder (Sequential)         (None, 28, 28, 1)         1047360   \n",
      "=================================================================\n",
      "Total params: 1,655,424\n",
      "Trainable params: 1,642,496\n",
      "Non-trainable params: 12,928\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "ae_model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"encoder\"\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "conv2d_2 (Conv2D)            (None, 14, 14, 64)        1664      \n",
      "_________________________________________________________________\n",
      "leaky_re_lu_5 (LeakyReLU)    (None, 14, 14, 64)        0         \n",
      "_________________________________________________________________\n",
      "dropout_2 (Dropout)          (None, 14, 14, 64)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_3 (Conv2D)            (None, 7, 7, 128)         204928    \n",
      "_________________________________________________________________\n",
      "leaky_re_lu_6 (LeakyReLU)    (None, 7, 7, 128)         0         \n",
      "_________________________________________________________________\n",
      "dropout_3 (Dropout)          (None, 7, 7, 128)         0         \n",
      "_________________________________________________________________\n",
      "flatten_1 (Flatten)          (None, 6272)              0         \n",
      "_________________________________________________________________\n",
      "dense_3 (Dense)              (None, 64)                401472    \n",
      "=================================================================\n",
      "Total params: 608,064\n",
      "Trainable params: 608,064\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "make_encoder(EMBED_DIM).summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"decoder\"\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "dense_4 (Dense)              (None, 64)                4096      \n",
      "_________________________________________________________________\n",
      "dense_5 (Dense)              (None, 6272)              401408    \n",
      "_________________________________________________________________\n",
      "batch_normalization_3 (Batch (None, 6272)              25088     \n",
      "_________________________________________________________________\n",
      "leaky_re_lu_7 (LeakyReLU)    (None, 6272)              0         \n",
      "_________________________________________________________________\n",
      "reshape_1 (Reshape)          (None, 7, 7, 128)         0         \n",
      "_________________________________________________________________\n",
      "conv2d_transpose_3 (Conv2DTr (None, 7, 7, 128)         409600    \n",
      "_________________________________________________________________\n",
      "batch_normalization_4 (Batch (None, 7, 7, 128)         512       \n",
      "_________________________________________________________________\n",
      "leaky_re_lu_8 (LeakyReLU)    (None, 7, 7, 128)         0         \n",
      "_________________________________________________________________\n",
      "conv2d_transpose_4 (Conv2DTr (None, 14, 14, 64)        204800    \n",
      "_________________________________________________________________\n",
      "batch_normalization_5 (Batch (None, 14, 14, 64)        256       \n",
      "_________________________________________________________________\n",
      "leaky_re_lu_9 (LeakyReLU)    (None, 14, 14, 64)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_transpose_5 (Conv2DTr (None, 28, 28, 1)         1600      \n",
      "=================================================================\n",
      "Total params: 1,047,360\n",
      "Trainable params: 1,034,432\n",
      "Non-trainable params: 12,928\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "make_decoder(EMBED_DIM).summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we define the loss for our autoencoder model. The loss we will use is the reconstruction error. This loss is similar to the MSE loss we've commonly use for regression. Here we are applying this error pixel-wise to compare the original MNIST image and the image reconstructed from the decoder. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "#TODO 2.\n",
    "def loss(model, original):\n",
    "    reconstruction_error = # TODO: Your code goes here.\n",
    "    return reconstruction_error"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Optimizer for the autoencoder\n",
    "\n",
    "Next we define the optimizer for model, specifying the learning rate."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "optimizer = tf.keras.optimizers.SGD(lr=LR)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Save checkpoints\n",
    "\n",
    "This notebook also demonstrates how to save and restore models, which can be helpful in case a long running training task is interrupted."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "checkpoint_dir = \"./ae_training_checkpoints\"\n",
    "checkpoint_prefix = os.path.join(checkpoint_dir, \"ckpt\")\n",
    "checkpoint = tf.train.Checkpoint(optimizer=optimizer, model=ae_model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define the training loop\n",
    "\n",
    "Next, we define the training loop for training our autoencoder. The train step will use `tf.GradientTape()` to keep track of gradient steps through training."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise.**\n",
    "Complete the code below to define the training loop for our autoencoder. Notice the use of `tf.function` below. This annotation causes the function `train_step` to be \"compiled\". The `train_step` function takes as input a batch of images and passes them through the `ae_model`. The gradient is then computed on the loss against the `ae_model` output and the original image. In the code below, you should\n",
    "- define `ae_gradients`. This is the gradient of the autoencoder loss with respect to the variables of the `ae_model`.\n",
    "- create the `gradient_variables` by assigning each ae_gradient computed above to it's respective training variable.\n",
    "- apply the gradient step using the `optimizer`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "#TODO 3.\n",
    "@tf.function\n",
    "def train_step(images):\n",
    "    with tf.GradientTape() as tape:\n",
    "        ae_gradients = # TODO: Your code goes here.\n",
    "\n",
    "    gradient_variables = # TODO: Your code goes here.\n",
    "    \n",
    "    # TODO: Your code goes here."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We use the `train_step` function above to define training of our autoencoder. Note here, the `train` function takes as argument the `tf.data` dataset and the number of epochs for training."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train(dataset, epochs):\n",
    "    for epoch in range(epochs):\n",
    "        start = time.time()\n",
    "\n",
    "        for image_batch in dataset:\n",
    "            train_step(image_batch)\n",
    "\n",
    "        # Produce images for the GIF as we go\n",
    "        display.clear_output(wait=True)\n",
    "        generate_and_save_images(ae_model,\n",
    "                                 epoch + 1,\n",
    "                                 test_images[:16, :, :, :])\n",
    "\n",
    "        # Save the model every 5 epochs\n",
    "        if (epoch + 1) % 5 == 0:\n",
    "            checkpoint.save(file_prefix=checkpoint_prefix)\n",
    "\n",
    "        print ('Time for epoch {} is {} sec'.format(\n",
    "            epoch + 1, time.time()-start))\n",
    "\n",
    "    # Generate after the final epoch\n",
    "    display.clear_output(wait=True)\n",
    "    generate_and_save_images(ae_model,\n",
    "                             epochs,\n",
    "                             test_images[:16, :, :, :])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Generate and save images.**\n",
    "We'll use a small helper function to generate images and save them. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "def generate_and_save_images(model, epoch, test_input):\n",
    "    # Notice `training` is set to False.\n",
    "    # This is so all layers run in inference mode (batchnorm).\n",
    "    predictions = model(test_input, training=False)\n",
    "\n",
    "    fig = plt.figure(figsize=(4,4))\n",
    "\n",
    "    for i in range(predictions.shape[0]):\n",
    "        plt.subplot(4, 4, i+1)\n",
    "        pixels = predictions[i, :, :] * 127.5 + 127.5\n",
    "        pixels = np.array(pixels, dtype='float')\n",
    "        pixels = pixels.reshape((28,28))\n",
    "        plt.imshow(pixels, cmap='gray')\n",
    "        plt.axis('off')\n",
    "\n",
    "    plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see how our model performs before any training. We'll take as input the first 16 digits of the MNIST test set. Right now they just look like random noise. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQIAAAD7CAYAAACBpZo1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzsvXl4lfW1/n0DSUgCJJAQSAIJIUCYBxlEwQFlRhyriGOr1Rat2tFq1Toda6UD1tZarbWeY4+ejk6gRSuCEyDzGGYChEBCQiZCCAkk7x/7+tx7B95TdnyvN/6u6/esfwLJ3s/zfIdnrXvda/i2aWpqUiCBBPJ/t7T9sh8gkEAC+fIlUASBBBJIoAgCCSSQQBEEEkggChRBIIEEokARBBJIIAoUQSCBBKJAEQQSSCCSYlrzZg888ECTJB09elTt2rULPUBM6BFOnDjhnyQ5devWTZJ05MgRtWnTRpJUXl4uSUpJSZEktWvXTvHx8ZKk3bt3S5Kqq6uVnZ0tSeratask6cCBA5Kk5ORkHT9+XJLUuXNnSVJpaak6duwoSerUqZMkqX379pKkkpIS33vu3Lltohnnj3/84yZJqqmp8XVOnjwpSX7Wo0ePqm3bkB6Oi4uTJDU2Nno+GCef79Chgz9fV1cnSaqsrPSz9ezZ079jXphHrlFdXe3n6dChQ7PxlpaW+rrz5s2LapwPPfRQkyTV1tb6d9yLa9XV1fm5s7KyJEllZWVKSEiQJBUXF0sKrYskJSQkeG+UlpZKCq1/UlJSs89VV1f7foyJsRQVFflzXIv/Hz582Gvx5JNPRjVOSbrzzjubJKmiosL7hrFWVFRIkpKSknTo0CFJUpcuXSSF1vTo0aOS5D3Gz8rKSiUmJkqS9uzZIym0r+vr6yVJaWlpksJ7oV27dv4878b+/fvV2NgoKbxnmasjR454rC+88MK/HWuACAIJJJDWRQTHjh2TJPXr18+WAG3Wt29fSVJubq4KCgokSfv27ZMkDRw40Npu165dksIIIj09XQcPHpQU1qCjR4+2xly0aFGz6+/atUvnnnuupJB1kEKooaGhQVLYkoEkSktLbQGiFTR6z549bS2xHjxjY2Ojtm/f3ux3bdu29TiPHDnS7FrZ2dl+RixlRkaGMjIyJElVVVW+riQdOnRIEydOlCRt3rxZkjRkyBA/D1YKy3X8+HGjrP8v4+TniBEjfP2NGzdKCqO/rKwsozLGyd+6detmhMS6jh8/3pYNi8vnS0pKdN5550mS982IESP8OeaM9aysrDQaaYmAtAYOHKj9+/f73pFjTU9PNyrFcldXVxsJsQ6gk86dO9vap6am+jlZ03Xr1kkKI4gtW7bouuuukxRGvzk5OV4Hxtq9e3dJIQQRidb+nbSqImCjrVy50pCNSfz0008lSX/+85+Vm5srSRo2bJgk6d1339WVV14pKQS3JXmT//3vf/c1eEG6devmSc/JyZEUfhFzc3M9wUz4gQMH/AKyMCitpqYmb6poJTY2VlJoo7CZBw0aJElas2aNJGnnzp3q1auXpPCm3rNnj2bMmCEpvJEY25o1a5o9kxTalIzlVNcjLy9Pq1evliRlZmZKkjZu3OjvnuoyVVVV2c2IVpiz7du3W4EOGDBAkvTJJ594TH369JEk9ejRQ5K0atUqXXrppZLkl2r48OGSQpv91Jc3KyvL4+RvuEKdO3fW2rVrJcnzefjwYe8TYDtKPyEhocXrKYVf3v3796usrExSeD/n5+dLkhYuXOixotw3bdqk888/X1JY6eKWHTx40AqAvRsTE2MDyAvN2p599tkqLCxs9rtt27Z5j2MEMDDx8fEe/5kkcA0CCSSQ1kUEwMGEhATDPyAOcK1Xr17WbLgB06ZN0/r165t9btmyZZKkHTt2aPDgwZKkxYsX+z5YBARLPGfOHC1fvlxS2OpfeOGFhlBoU9yBoUOHGgJGKzx/VVWVLcnevXslhS39xRdfbHgOmZaXl6ePPvpIUtgaAKsrKio0duxYSdL//M//SAq5WFhxXC2sTl5ennbu3CkpbIn79+/vvy9cuFCS1Lt3b0kh12nHjh0tGifP3bZtW6Wnp0sKQ3fW+rrrrtOqVaskha3Yeeed5/XEIjI/69ev1+jRoyVJn3/+ua/PPuE+jA0UIIXR3PDhw41QIBWZg+zsbJOKLRFQ3sGDB08jedkfo0aNUlFRkaTwPh00aJDee+89SaE1l8LuUGVlpV1YkFptba3/jkuwYcMGSdJNN91k5Mw6jh8/3t999913fU8ptA95N84kASIIJJBAWhcRYHX79etna8/vsJxDhgzR0qVLJYVDJHv37rXF4/NYmd69e9vPxDrs2LHDlgP/GytTVlZmnxmt/cYbb9hvw/fctm2bpJBVR/NHK/ixnTt3thXE+sON1NXV2Spyz549e9rCQ/7hG/fo0cNogdBoeXm5LRXPiE+4atUq5eXlSQpb5y1bttjKcA2szaFDh1pMimK5cnNzzTWwTvit27dvd2gMBHbOOecYsWHNmO+uXbt6XljDlStXev3gXLD4ZWVlHhOWesGCBeZK+vXrJ0nmEcrLy1vMhUhhbionJ8fPAPrBArdt29bzz1x27tzZXNfHH38sKYx0R44cqS1btkgKo8g1a9Zo5MiRkmSExr7+6KOPvH+Y+0WLFjUj4fmdFNozpyLj/00CRBBIIIG0LiLAKtbU1DjUg4+KLxcXF2drCCLIzMx0kgTXwIrGxcXZOvz973+XJE2dOtV/h0vg+sOHD7d1iUQSAwcOlBRm0Qkxrl271lGMaAUtnJWVZfYchp0owIEDB/yMY8aM8XfR+LDcN9xwg6QQ+04I9I033pAUskj4vnwexJSXl2eUA6sdHx9vq4FFueKKK/zMsOHRCvN+8uRJzxv+N5axR48eHtO0adP8+XPOOUdSmBu45JJLJIUQCtfAWnbu3Nm/e+KJJ5p9fsSIEUYOb731lqSQhSZ6wbpPmjRJUojFJ5z3Rcbau3dv712QH5b7rLPOMiK64IILJIUQEc8CEuVnpMX+4IMPJIX4E3gTUC/3u/nmm410uGenTp0cUePz1157raTQHgMxnklaVREwwPLyco0aNUpSOJTy/vvvSwoRJBAcwOr33nvP5BkTMXPmTEnSn/70J5199tmS5Am55JJLdP3110uSfvOb30iSQ2k1NTXetLwMkcqHF5fYdMeOHb1w0QpEzqFDh/zcwD2IvmnTpvmeEF/l5eWGeYQ2WcijR496s6AQsrKy9Pvf/16S9PWvf11S+OVpaGjwmNlI/fv3dzwcYpXnat++va8brRA+27t3r5UaLz1E36BBgxwGxM0pKipqljcQOWfV1dW6/PLLJYVdmszMTCu///iP/5AUVngrVqzwc+Pq7dmzx3PLWo8fP15SyCXClWiJ8JwbN270mmKccK/Wrl1rpR5JvEJ0QkSjxP75z3/afcM1zszM1MMPPyxJuvfeeyWFleW+ffvsVjDf8fHxVoCEIAldNjU12QU8kwSuQSCBBNK6iGDlypWSQtYWcghoSmhs06ZN1thY/yuvvNIECBabhKKUlBRbHzS0JD344IOSwiElYFP79u119dVXSwqhCSkUquP6IAEs68iRI6NOykAic88R3Ass05tvvunkE8imUaNGGZ4Tcho3bpykkDWYPHlys7Gcf/75mj17tqQwOYX1z87O9liwvvHx8f47zwM07du3b9RZaAgWbtiwYbZU06dPlxTOOly2bJlRAvD+0ksvNSIBJUAuZmVlmSwkLHfuuefalcDqMz9DhgzRWWedJak5UclYsN7cp3fv3ob5LZFNmzZJkiZMmOBx49b2799fUggZ4KrhrmRnZ+uzzz6TFCZLmft+/foZlbK2vXr10te+9jVJYURESHL48OGaOnWqpFDinRRaW+aXvcJapKenm4Q8kwSIIJBAAmldRECqZXV1tYkWrBvhrzFjxjg8s2TJEkkhDYqVQHM+/fTTkkIW5Nvf/rYkmRdobGx0KOkf//iHpLAvvH37dj3zzDOSpB/84AeSwkk7UjitlZBPfn6+fa5oBX9XClvG559/XpJs2S677DKHn/Anu3Tpoj/84Q+SpO985zuSpJ/97GeSQpYIchNr0L17d9dcvP7665LCJOfWrVu1YsUKSdKNN94oKUSOcg2+R4LT9u3bmyXnRCPMUXV1tbmMv/3tb5LCPuzIkSPNS7DWbdu2NQL7xje+IUl6++23/fysP753dna21xMUBwGXn5/v9QNdduzYUVu3bpUkoy4QQWlpqf3xlggE8+bNm40o2JPs3QsuuMCIFVR4zTXXeM7hxb75zW9KCiWywSmwJy+//HIT4qAm7v3uu+/qtddekxTmwzZu3Gg0zTyDJNatW+c1OpO0qiKA2e7Zs6dfEAbxz3/+U1IIJrOIs2bNkhSCtJdddpkk6dFHH5UUJguPHDlioozf3X777XZDUBzkJtTU1HiDEcNu166dCR1gHi5FVlaWiadoBXg/bNgwL86QIUMkqVkJNIw2iqNHjx4milAIKI7CwkK/PJCXXbt29eaCYIUcS09Pt2vCWHbv3m3IjNJkrtevX9/iHHw2eI8ePQxBgcu8LO3atfMzoYz79Omj733ve83GSa3BypUr/RIBdceOHWuyDteKWpLs7OxmhVmStHz5csNjrsHzbdq0ya5SS4Q17dKly2l7F8VVWFho4pI1ysjIsLvEy/6Vr3xFUkgpQfChwLt16+ZrEHkg0rRy5Uq7RCiLTp066V//+pekMAlJhKp3794mos8kgWsQSCCBtC4iAIaWlJTYGhIiRJsvWbLE1gotXFJSYiKIODKk16pVq/Tzn/9cUhhGf//73zdcwwpxrTlz5jikiCZ/5ZVXHLIiTIUl3r17t0M80Qpj2b17tyEzBGhkBh6kEbB3+fLl1vQ8N6ilurpav/rVrySF0U1RUZHhI4RjpIUBMgOFq6qqDBUJO2LVYmJiWmwpCR+Wl5cb6ZxKTq1cudKVhszFhx9+aKR00UUXSQoTw0VFRXaLCLVu2rTJ6w8ZBwF3zz332K1AduzYYeTw6quvSgojw5SUlKgJtEiJrCZk3Fhefh46dMjuL6izvr7en8dik2l58uRJ7y0QcWpqqt555x1J4T24YMEC/593iD1WU1PjGgbmKzJPAoR2JgkQQSCBBNK6iADrHxMTY4tN+AoLP2vWLP32t7+VJIdRIvPP+V6kJcG6YOFnzpzp0BOZdFjKX/ziFw65oamTk5OdlMRz4DuPHDlSQ4cObdE48bUTEhLsK2NtsYpt2rQxsYQvOGjQIGcB4idGNjaBVAT5fPLJJybnsBRYgNWrVzvUxu+Sk5PtuxJuJCyWl5cXNbGEYFljY2ONeCAJqYsYN26cuQ2uP3r0aFs9eAzGOWvWLK8jPMDs2bMdzsXnxQp+/PHHXjuyQxMSEoyQQGDwGT179vxC1YcgNfaTFN4jVLOOHTvWXBNob9euXd7j7AX4kPz8fJO2ka3NuAfPSXLZhg0bHG6Ey1qxYoV5EOac6xcUFESdFRsggkACCaR1EQF++tChQx3aQluS+LF8+XL7d/i/Z599tq0b9dho+MTERCejYCV2795tn47kDxKYxo0bZ6tCj4Lhw4e7Ph8rRxiR+0hhP/NMQmi0Y8eO9gf5Heiirq7OGh//cPLkybaM1JbDOO/bt89zRDjwnXfeMcKAB6C2YuzYsX52+ID27dvrww8/lBSOMrAmx48ft2WDLzmTEAWKTJoBiZFO3r59e1so0EdSUpLDf6RBgxaqqqpszUkai+zORM0AYeHs7GxHdWjjNXPmTP3nf/6npPB8s6579uyxT98SwRKfc845DmHCi5C4VVlZ6f0Mkz969Gjvxd/97neSpDvuuMNzxN9Yv8TERP8bBA2qyczMNKcAlyVJ//3f/y0pHJ4kqtGhQ4eok+FaVREAd4uLizVhwgRJYeKE8Nq2bdv8MgD5cnNzveFxEfhbTU2N6xSIGS9dulR33323JBmCAp23bt3qMA5x508++cSTD2nFIu/evbtZnkE0QqirV69eHgsbkkUdO3asX1A2cHx8/Gm5BcDCwsJCE6xk0h04cMBwmDoEFNixY8d8z1tvvVWS9Mtf/lIXXnihpObNMaTQi8LLHK3gBmRlZXk9UbgorcGDB5vEQsF36tTpNLjM8xw/ftwKGmWSkZHhcREWZtMXFBTYELAPXn/9dZOtkX0ppVD+BC9pSwTlsWfPHs8rxol5SEpKsmJDYXXq1MlrCPGL8k1PTzcJ+tWvflWS9Mc//tHriyJnfKmpqVag/Jw/f77zDHBrCRe3b9/eLtWZJHANAgkkkNZFBFRRFRYWasqUKZLCYRkyv2JjYw0DQQEPPfSQyUG0MPUFu3fvdsIF5NLkyZP13e9+V1IYqj7wwAOSQrCJMk3COSUlJSZtIBAhfVJSUkwURSvcs6ioyNqcppNY/DfeeMP3hAD9+c9/7pAR8BwS6cCBAyaFeLba2lpbca6P1S0sLHSYFBTCPaQwosJ1Sk1NjbqJxanjPHDggMN6ZCdyzxUrVhjiEiZ98803jZrIycf6NTQ0eE+AykpLSx0y5XPXXHONpBDBCvq7//77JYUsI/fEkv/1r3+VFEIvLV1PKVzOXFtbazeP54tsz4abQgLcM888Y/SCqwNRu3jxYqMLEoVSU1P9b+bwtttukxRCI7iMlHTX1dW5+hBkQgbngAEDTLyeSQJEEEgggbQuIsAfkmTS6qabbpIUrpg6duyYNSy+7dq1a51AQWgF//HNN9/Um2++KUmuzNq9e7fDheSyQ4R16dLFmpPfZWZmmi+AoCTcV1xc7PTPaAUfLS8vz5VjIAPCbEeOHDFvgY93/fXX2+eH9ItsE/7yyy9LCiOZhIQEcwPwI3Ao+/bt8zh5npMnTzpFm2th6aRwKmu0wjolJSWZ0yChBu5h165dJupY661bt56WEg1HUF1d7ecltLhv3z6jQ5JlCL0mJyd73bnWoUOH9NBDD0kK8SJSmCNKTEw0smqJsA6dO3e21QfV4psnJSXZYjMPK1euNBHKPDC+nJwcvfTSS5LCofKYmBjfC6IY3uXEiRNGS6xbaWmp7rrrLklhgpmQYWVlpRHmmaRVFQFscG1tbbO4viQXDq1cudITcfvtt0sKwTtIIjYYG2Po0KGGuZB6O3fu9PkATByb5NNPPzUxicTGxvq6/MTNGDt2bDMFFo1AijY0NJh8JFLBC7Br1y5v/hdffFFSiBRDqTEHkI0TJ070yw7c27Rpkxl5cgWIJaemppqww43q37+/yaxT57F9+/bOYYhWUMrt27e32webTan3Z5995rHMmTNHUsgtQYnwPCjMxx9/3Kw88H7VqlW+HsohkhzFtQJST5482YYGGE8DnK1bt3ot2HPRCPkXdXV1fgbGyt8is1DJgE1OTvZLy4uKuzJs2DArddyFFStW2DCwd5mHJUuW2CXg7INI5cNYKUq79NJL/bszSeAaBBJIIF9Oz8LRo0eb9KESi+yzsWPH2ory8+WXX7aFx4IBB5OSkgy1gIOvvfaatT6WEmJr0qRJtsrE46+66ip3tj31VJqTJ0/6OaKVyKpGchuwWliRlJQUIySs88cff2wrSywYkjQxMdElq1j6hoYGh6aYW6zVmDFjHEN+7rnnJIVCU6ADLAuuRHl5uQm8aAWInJ6e7pAXz8h6Tpo0yYQkVmzJkiW2nEBX1nX37t1Ggsx7586dPacIFj4hIcEIjHHOmTPHoURyP7CaAwYMsFvZEmGPDRo0yAgRZAl67NOnjxEcbtlbb73lvUW+Aeg0IyPDn2OfZGRkeC35HOTwjBkzTDy+8MILkkIhcBAG42LMnTt3DhqTBBJIINHLl3LS0datW+0TQqREntcG+UIO/tixY23ZIdEI833nO99xBR1hxwMHDhgR/PjHP5YU9uMGDBhgggY/Mjc319cn+QU/7eTJky1uZME46+rq7MtiKbH0KSkp9snR6JGNJpkDmljcf//9JhJJlolsXElYlTBeWlqaa/0h8vr3728LATKBY0hISHB4L1rhWgcOHHDiCqQciKCpqclrS6issrLSYUYyBLHm3/rWt3wtiMG6ujrPB9wSczF69Gg3qQHtdO/e3aFQwo6QtV26dIm6s2+k8J19+/Y16xQthUO3x44d83NiuXv16uUMUmoTCAc+9thjRkQ856FDhzx3kH7MQ25urrkHwsaRXbxJxAIlHj9+POrGuwEiCCSQQFoXEeDz1dXVmRGFLaVmvaCgwFYKTTtmzBj77PjHf/zjHyWFfOhTqwNvu+02d/pBO6Khjx07ZmuCdV65cqX/Tmoo1i4+Pt6hzWiFZpLt2rWzlSVCAAdQV1dnFIL/m5iY6M/h95Iz3717d/MX1GlMmDDhtLMVQVjLli1zUhJ8zO7du+2LEh4FRaWmprb4XANQSGVlpcfCOhH6ikxxBSldccUVtmggmKeeekpSCMnA/xAdueWWW2wJQRwkTh08eND3YpzLli3TD3/4Q0nh5Br2W1pamueqJQIiqK+vN4sPTwGPs3HjRltgIhiXX375aRWAnHg0ZswYX5d56NKliy07zwzvc/DgQe9P+Jby8nKvOUgRPqpjx44OPZ5JWlUR8IKkpKQYJs2fP19SOId8+PDhJrB4YWtqajyxhEPIte7bt6+hIXn0f/nLX5wXwMIAt7Zs2eLYMuGsCy+80JmNXBc4u2PHDuept1R69uzpsQAn2UQpKSl+GRlTSkqKYTyEEQTe1VdfrVdeeUVS2IV46623mrUyY66kUHiSBi/UW8yYMcPx98izF6SQAgZ2t1TGjBljF4gxQXQlJSV5s6NoBgwYYOUHrGWzT5482SFl8hpeffVVZ4NCgBJ2XLx4sR577DFJ4YK0sWPHesyU6+I2tG/fvsWt56Tw3m1sbDRZyB6ht2b//v39t6uuukpSiEilKIi6B3JJbrjhBrtGzE1tba3rNlBskWdRPP7445JkRXfllVd6LjBmvFsxMTHBkWeBBBJI9NKqiADLmpmZaaIMbUdoZf78+YblhEPuuusuJw0Be4Drka3KsPRz587VPffcI0n66U9/KilMvt12221O2kFrL1261NCMbENgeGxsbItz8CNrJbCQXJ9Q0rJlywzrITsHDRpk14DxggzWr1+vW265RVII8Uih040gN8ljjzwAE+t05513SgqhrlOTo0BAbdu2tYWLViIPhwWykzyD27BixQq7hIQYk5OTDedZR1y+9evX+4Qf1vP555/XvHnz/JxS2M2YPn26DzilxdmCBQtcx0HLMO6dk5NjaN4SAbEkJycbluNigDoKCgqMdBjXxRdfbPcXdMf+Xrt2rROKQGp333235s6dKyn8ToA4xowZY3TBiU9/+ctfnOEIKmOfvP/++1F34A4QQSCBBNK6iADrv3fvXpODkBlo2SlTpthn5ufcuXOt9SH/sJyTJk3ydyGZysrKbIXwq/n/gQMHrGHhD/r06WOLjRVCk5aUlPi5oxVIpNraWtcpYCHwTy+44AKTYfh0GzZssLXA7+P/cXFxRkj4zjt27DA3cGp7r+nTp7vunp4PF1xwgYlVmnpSubdp0yYjmWgl8hBQUA2JWyTBTJo0yYiKZ3zppZeMJvgdc9axY8fT2pF99tlnJtDgCmjR1q5dO88BeyQ5OdnjpBkI9QVvvfWWffWWSOQBvlhq+ApQ1ogRI8yHMOYnnnjC+5MkLupZLrroInM88AIff/yx9zZNSwkfFhQUeC8SGh4yZIivD5rl84WFhZ7XM0mbUzO2/v+UH/7wh01SiCUHusG4ohBycnIMfWGiu3fv7sWGtAFSPf300846BJ6OGjXKMJcXg5c/shMsuQy9e/f2dVlAXsSRI0f6ZZ46dWqbaMZ59913N0kh14B8B15irt+jRw+f3hx56jMbHmj9/e9/X1LoPAfcFVyKxsZG5yCwUVEqgwYN8kbFLRo9erTJOeL45BN06dLF8zh9+vSoxvmd73ynSQopWV4A8i8icxdYT4jS0aNHez3YtLhpP/rRj0xy4i706NHDRCYvCePs16+fiUnGlpqaahKUMmeU4pgxY+wmXHHFFVGNU5K++c1vNkkh5YdyZj1QfgkJCac1PRk3bpyJS8YKkf33v//dc8JYZ82aZSXKHmD+pkyZ4pc+svdlZIRLChOj48ePt1ty1113/duxBq5BIIEE0rquAdZrw4YNJlVObW01YcIE53UDPevr640IiP2j9S699FLHirnWK6+8YtcDuEQWWGRPOBBBQUGB++mR+839+vbt656CuB5nElDA1q1bbakhR3FBIjvpgo4qKipsBYCvlKfOnDnT5BRQf/To0Z4PLCD33rZtm8OvxPLXrVtn2I1bxPf69u1rawaRdyaB/I0c56nZiRUVFYbOrOeePXs8ZsZCnciNN97odYkM+RErxyJCuu7atcvwl3DbkSNHjA4YO3uvsrLSKAjLHI2wXiUlJZ5Dnh10lZub63Fhifft23famRaEO2+99VYjI/b8vHnz7NYQUmeeDx486P0J4dmjRw/ve5Alocjy8vLgpKNAAgkkemlVRIBfM3z4cIfVyJTCX96zZ4+rp/Azx4wZ42QJtD7ZdU1NTfrWt74lKZxF9txzz/nzEEhY2DZt2hh9QEZ99NFHTrAhFERtQFVVVdTtnhCsVffu3T1OsvywAB07dnQmWCRvgOWhMpHnjo2NtQ+PTzpv3jwn30BGgjwyMzN9XUKLpaWlRhyQocxTZWXlaRV+ZxIsfI8ePU7r6EvmZ0JCgtcTyzVs2DCjMiwoz1VUVGRijyzM5557zpWijBPUkJqaahSE779+/Xr/Dp4JhJeQkPCFag0YX3p6uueOPQMf1bFjR6Mp7peZmelnIbQa2bCV0C7rtnPnTmciwltB7F566aUeN017/vznP3tOqKrlfjExMUaMZ5IAEQQSSCCtGzV46KGHmqSQRsSa4P/QWeeSSy4xQxx5glFk7bsURhIrV640a0oIq0OHDvbf8JuwrOvXr7fPhm+3b98+a2GsFlYjPj7eGvahhx6KimW+7777zDBzL56RBJdZs2YZ8aD58/PzbXmwbvh73bp1M5uMD3n06FH7olggwnJHjhyxfwi3sWHDhv/VGkbWf8ydOzeqcT7wwAOlKb5pAAAgAElEQVRNUsjS8Rw8f2QYE6afNd+5c6efjbRw5ri4uNhjARFkZmY6RRsLh1VtaGjw3oBH2LJliyNDcBc8Q1JSkkPQDzzwQNRRAyJBkREvxkp0JiMjw4lHPNPmzZtdNwLaI0zbtWtXp0WDTrOzs82DMF/snbKyMt+TeUhLS3NEij3O944dO+b3a968ef92rK3qGvByRsY2CZ9EZrgxcGD65s2b3RWWDRaZIUeREjHj0aNHG55zfTbCsGHDvDkgaPr27WsykQ0NibZz584WNyaBFIqPj/emQSGwKXbt2mUozjPW19fr5ptvlhSG1jS/OHDggJ+RzMixY8f6pUGR4TZkZmZ6LBBmkS4Zc4zSLSkp8XNEK2y8uro6uyFsdhT2zp07vd4QdomJiW49Rkyd9a+oqLACoL5k3LhxfjZITl6Wfv36eZwo2V69evmezDEvR2FhoeesJcLLHhsb6zVljMxpVVWVyWDukZKSYpeOPpEYoKNHj3o/vP7665JCSp45ZL549iFDhtg1RiF07drV+5/xM/Z169ZF3bE5cA0CCSSQ1nUNAgkkkP8zJUAEgQQSSKAIAgkkkEARBBJIIAoUQSCBBKJAEQQSSCAKFEEggQSiQBEEEkggChRBIIEEolZOMaajTX19vdM0yc2OzCUn9z4yf510S1I3SaNMSUlxCia1CbW1tc6xJtWY/O4OHTo4T5uKt0OHDjnPnvtE5r6T3vrMM89ElZv+2GOPNXFPUnnJfY88BYlx0rmmrKzMtQKk7/L/lJQUzxl57BUVFX7OyOPWpVDaK/eMHCfpq6TFMselpaVOx33kkUeiGuc999zTJIXSlVk/njfyHADmlHseOXLE+fGkP5M6nJyc7Dki/bmqqsp1+IyTtY48j4F71tbWel+RHs5+OXTokHsmPPvss1HXGtx1111NUmj9GAf3Y11OnDjhGggqXOvq6jwenpP1Zm9K4ZT52tpaj5E5Ir05KSnptDMVSktLvQeYe+prCgsLvdeff/75/3NqDXgZMzMznefPolCSm5yc7MIfJuTkyZN+oXhBmKSePXt6U3DNIUOGWBFwrgCLV1lZ6XZalBePGDHCLw2LxQZq166dy02jlciSVcbMZqEwJjk52S8BGyIrK6tZbzwp/BJ36dLFc0A9AaXSUlg5sPCHDx92WSo1HpHty049LvzIkSPNNmZLxpmTk+NnYp55Efr37+/8eH5y/Jh0emluTk6O14DfZWVludBq4cKFksJnXmzfvt0FOxTwDBkyxEoNBUUNQHl5uZVfS4Tr9OvXz8/My8uaDhgwwOXxKPnIsnf2KS9s5L5i/s477zzPIS3meNH379/vYwCpubn44ou9vuw1xnfw4MGoD3xtVUXApjtw4IC1PS8lbbh37tzpohMKUT7++GPXo9Olh2KcNWvW+CVjYrt27epFwjJglRobG60cuE9RUVGzAyykMPKIj4+3lY1WUCJ79+497Zw8zm4sKCjw/dkon3zyyWk9B9jwO3bsOK37Tb9+/dyBCevEwnfq1Mn9GXgJSktL/XKhOFCeiYmJLW7SCrrYsmWLNyEnEHGu5Ntvv33aei5YsMCdl5h3ispeffVV9xBAMfXp08cdpPr06SMprCS6devmylVemO3bt3uOUHgo1iNHjrS4SasUnvP8/HwbIxQtrchfe+01jxVlt337dvcoYG1QakuWLPEc8m6kpaXZQLCmfKZz584utgLB5ufnn1YNGWn8gg5FgQQSSNTSqogAyFNXV2crSMkuGvG8886zFQXyXHHFFYZ91JIDjVauXGkrimZu3769XQ7cDDriDBo0yJ2CsSADBw40gqB7MRq9d+/eLT4ii3FKYX8NSI7MnDnTmh+/b/r06S4lxXqAgDZu3OhuynQ5at++vZEDrg1z16dPn9PgceSRXFgWSmlzcnJa3OY7sqSZ52X+IjsQg7JwDaZOnep+hLgy77//vqTQmnNgB63oO3XqZFQGEmA+r7rqKj3zzDOS5O+NHj3aY6dkm30zbtw4d/lpiWB1Y2NjXTrMPRjf7NmzvV7A/5EjR7oXJAiC4/Xq6+u9L9nfcXFxXgfcQlyEvLw8IzjQwrnnnuv3BAQIKsnNzY3aNQgQQSCBBNK6iABt3rt3b/tsp3YSysvL05///GdJ4WYbxcXFthz0s9+4caOkkIWnLyHXKCgosEXHMtFopLS01J/DUv7tb3+zz4V/Sh+4kpKSFpOFjK13795GPNyfA0xLSkpsmUAEPXr0sKXD2oB80tPTjRZgnTdu3GgOgftERiVAWVizt99+274unAWopLq6usVkYST6YHyMHYSSlZXljruRXXaxojwvSCUtLc1dhuGBNm/e7OflnpGNRkBK7K93333XfjXryX0+/fRT8xItEfiTzMxMrxvELmtaXV1t/5z79unTx5wRcw1K7dGjh/cZe33Lli0eD+iKudyyZYv5Htb2rbfe8l7hnAW6btfW1jZDp/9OAkQQSCCBtC4iiOy/hz+PH4TVio+PtzbnIFApHGYkdENPv02bNtnPIrQ0ePBgW7ff//73kuSjpXJyctzPnu6wY8aMMRvN9SPzCGgtFa1gmRobG+0Xo8Gx+JGnK6HJ27Rp4w6+fA/GOT8/36gFNNS1a1eHSemVzwlQjY2NvtZrr70mKWRhsVQI11y1apVj+dEK69m1a1f34D+VE+nfv7+vyxzMmDHDyI4+jITFtm7d6mf6zW9+IynUhRmOAJ8f5r2xsdFdkrGgeXl5Xj+sJd8vKipyTkJLBATTuXNn+/js08h5YIzMQ1xcnCNXIAf22sGDB/05oiLdunUz2iHyQlu3oUOHGg3Cn+Tm5nr/gK45BDU/P79ZqPbfSasqAhansLDQMXAelHbmWVlZhq+QJp9//rnP+wPiMakrVqxwDgLKYfLkyT4qjDPicB8qKyu9mVBC69at8yQC93i+nj17Rn1+HIJi2rdvn18CNs1nn33mcfM5WlmXlJR47GxgIPGqVav8YgOP09LSPG+/+tWvfA0pFG48NW69c+dOvxCnNg/Ny8vz87R0nMXFxX423CgIze3bt1uJQ5a98MILDhufeuTXmjVr/HmUwwUXXKDvfve7kuT27bw4DQ0Ndj1QKpHJV7hWhDUjG4y2RBjX2rVr3Woewo69VV9fbyXG/T/77DO7S7gUKIKVK1fquuuua/a3UaNG6YknnpAkk6AonsWLF9s1iDwD9O2335YUdj95DyLn4UwSuAaBBBJI6yKCyGQgSBUgHp1YCwoKbCWwfFdddVWzY7skmVDp0qWLtSmkSkNDg+655x5J4WwrQjjjxo3TzJkzJYVIJal56IxrcHDltGnTWpxQBAE0YMAAWykOGUFDr1271lYfQueyyy7zfSH/QELZ2dl2CUjGGjNmjC0xc0aC0YgRI2wFGXtqaqqtLSEm0Ehubm6Lx8majBs3zrCcQ2157nfffdfuC6jo+uuvN+ylUzFWtm3btk7mIvSVkJCgH/3oR5LCVhg3cObMmR4LB43U19c7ww/CEQLyuuuua3E4WAofPzZ9+nQjANYUFPmXv/zF42ZfDxo0yKFB5ujrX/+6pBCS4lkY6wUXXOC/Eyqk0/P06dONpEBBOTk5RrOgKtzK3NzcqInRABEEEkggrYsI0JZFRUX2L0kPxefPzc31wR8QijExMXrrrbckyf4TvvGYMWOMHDgLoGvXrtamhCKxDJs2bbKVvuaaaySFrND8+fMlhVNdsV47duywTxetcK/jx4/bgkFM4tudd9551tyR/ejhEL761a9KClu+vn37mh/BOqWkpPj6EIKk8W7YsMEIgjElJCTYevAckHrFxcXmcKIVCNijR4/a8mCV4TZmzJhhC096dXp6ukNcd911l6QwAhs1apQt2yOPPCIphPpY47/+9a+SwhZ31apVRoTf+973fB/mlrXAx9+0aVOzA2ijFfiKXbt2GWVyXDljveWWW4xEIhEo+xgS7+GHH5YUSjbiOUFGbdu2NYH4wgsvSAqnmS9atMhoBETc1NRkDghugD2Un59vtHQmaVVFQJFPenq6iQ0KNoDTSUlJhktA+MGDBxs63nbbbZLCcdd9+/Z5o8GYn3XWWX5pOBGJzbJx40az+sDkTz/91MwvEJuXoqyszIojWmFxs7OzTUyxWYhKVFdX+3PAvX79+hkWLliwQFL4JKhNmzbZtaLI5sILL3RmIGcY4jZUVFT48yiH+fPnG/5zKhSbaMuWLZ6DaAUlkpWV5TwAlD0k3eDBg53JyfmPaWlpJv9Q7JGVc8D4a6+91j9RlpBrkQQabhSRoqSkJDPv/MT9SklJafGBNVKYiMzKyvJ9yDCEDGzTpo3JXl7epKQkzZ07V1JYETD3y5Yt87pxrYkTJzpCgfFDuezevdvrB2lYVVVlNxv3mihXRUVFcPZhIIEEEr20KiJAWxYVFdkqo6lxFT788EOHtNDcS5cuNeTEqmAp33vvPUMt8tWXLl1qywsEJm59xx13+Hgp4Ob27dsNIdHCxKa7devWYteAENKhQ4dsLU61Qh999JHzGbBaS5cutXUjJArkXrdunaHyf//3f0sKwX+IJMKwuBI//OEPjZQiz5AEfeCqkKeQlJTU4ow73JKqqipbMYgzLPyLL76or3zlK5Lk+P3OnTv9bLhnWLgXXnhBTz/9tKTwMWDf//73jQhBdozpq1/9qt1EEOfzzz+vK6+8UlI4Fo8V/vzzz03ytUR4vj179nivQvbyc+nSpYbslNB/8MEHdnWYh8geFRC6ILmCggJDe+pBcDOuvvpquxmcOr18+XKjMHILuE9NTY3X4UwSIIJAAgnky6k1iGw0AqkHAXbeeefZP+YM+MzMTPuc+D8kztxwww0mggjTPPTQQ+YcIEsgiN59910Tk1jHpUuXmlAjpIiG7tKlixOPohX4iY4dOxrxkNFH2GfixIlOBEG7Dxo0yMgH6wmyufrqq+3z4xNee+21JgTx7wnfffTRR06Kwhr+5S9/MbphfPj2Xbt2bXHDDvzP4uJiowlQAoTa1KlTTQR+61vfkhTicCD9yJpDrrnmGj8TaOf+++83goBTwm/+9NNP7V9DIKakpBgJknjEfhg4cGDU2XaRAm/VqVMnW1nQHmhz8ODBrgCESB08eLDvfWrCVmSjGPii4cOHa/bs2c2uC3H8/vvve/zwFGlpaUaWvBO8K927d486KzZABIEEEkjrIgK06ogRI2x5SbLA5z969Kg1HJbk4osvtnUj3Ij2r6mpMftPbcKmTZusrbHOhM3Gjh1rBMEx1bm5uUYhWDZ83W7durW4ow3WOTMz0wk7JBKBTE6ePGl/kpBQQkKCWWTQDeOsrKy0NZg6daqkEOvMsxEZYH5ycnI8LzfccIOkEPLA7+bzzFNJSYl92WgFNDds2DCHA/kd6cS1tbWaNWuWpDAvMW3aNHMyhBT5f8eOHdW/f39J4fTn1atXG1nRyQqEwGelcCiyX79+5hLgZvDZGxsbHcXA8kYjzNPQoUO9V0AGIJLExERHh4h8TJ8+3X8nRA0y2rZtm6NfRBmOHTtmBMF6sKZpaWlGOo8//rik0Lv085//XFKY12INysrKfK0zSasqgshiD2A5ExzZYgtoM2HCBEmhMBy/A3oSPorsWYgi2LZtm6E+RAsb4cSJE14siKp33nnHxRxkGOK6HDlyxARNtILrkZ2d7ZeRxYF0GjZsmDfGtGnTJIUUAXAbyAh5mJqa6rEQkmxqarIrwLXYWI2Njb4X8e5FixZ5TnEzUEz19fVfeJxt2rQxFD61Z+GBAwfsykBiJScnOy4P0RfZWo6cCNakoaHB46QQiRenrq5Ol156qaTwy/H6669b8ZN5iVt6/Phxz9UXGWunTp3cCIeGIcxzQkKCyclIYpB70y4PI5iammr3EHclJibGIWHcH+pVNmzYYAX7j3/8Q1Jo3XmXcBNZ4+LiYiuRM0ngGgQSSCCtiwiwbps3b3bIAwuIdk1KSrKFRzM+8MADhrIQWmjJJUuWmBDkGhkZGXr++eclhUNzwNOqqipDZSBaYWGh0QqIAwSSlJQUdQUXgnXbv3+/n5NsMebg5MmTtvqgojfeeMNjZkwgky1bthg1MaZevXoZbnNPyLfKykrdfffdkqSnnnrK1wCZgASwYF26dHGSV7TCOq1YscJoDJcK+N2pUyffE7frvvvuM0EKvGZ933jjDVtfiL6rr75aDz30kKRw5R4uQUlJib7zne9IChOl1dXVnnesKs1AEhMTW7yeUriyb9euXUYArBWVnN27d3ciGvv06aefNkrDmrN+e/bsMRKALGxsbDRhzE8+M2TIEP3617/2d6VQOJU9BQqj4jYrKyvq+pEAEQQSSCCtiwgiG21GVq5JYQt1+PDhZq2qpZD2J7UUzY6/lZeXZ61/assqKcxF4CvV19ebeCQR5Pjx405nxg9Ha0cSQNEKZGFycrJTU2kegcUsKiryPe68805JIWQCEiC8F5kqe2oe/+HDh23dCBMtXbrU88PcYj1KS0v1jW98Q5L029/+VlK4WUjHjh1NNkUrEHIJCQkmRZlHuJaKigpzGqzP+PHjPR98DpJ027ZtJhBJpd67d6+vQTg1sgEooVkQR1JSkglnOAjmqbi42Ek8LREsdmZmpucTnoP1LiwsNCJgHt555x2Pgz3PtVJTU00Kk+RUVlbmNWf9INTbt2/vtYeL2bJli9ccjiTy8BeQ1pmkVRUBcK6hocGsL8TQN7/5TUkh6EVm3ksvvSQpBI0gvCJP8pFCm4rSYTbLrl27PAGRnYykEIsNtIuE/ywu2YaRUYdoe8MjvMxt27a18iNjEab6448/diyYOPHw4cNPyxVAIXz3u9/1BkGZLF261JmBwFSy0uLj4022wWBPnjzZLxCC67Fz584W9yxkPY8dO2al8Lvf/U6S9OCDD0oKRQW47n/8x39ICrkDRC9wxWgWM27cOJ9hQd3HkiVLTA4C//n+9u3bzcpDlg0dOtQGgwgRblheXt4X6mJMVCMuLs7uDPPKS1xUVGQXjSzQvLw83xuDxXzceOONfhZ+V1xcbCVJxAPFsXHjRv8NozBq1CgX1nEt3oPCwsJmRvHfSeAaBBJIIK2LCIjp9+rVy1aTn8SHL7zwQmt2rOmaNWtOy+GOtNiE34CX8+bNs9WEjMR69ejRw1Zizpw5kkJlupSBAo/RpHV1dbZa0QqopWfPniZwrr/+eknh3vPTpk1zRR2afPHixSaWQDJkox04cMAkJy7Tm2++afeG7Eegac+ePT1XwM/77rvPsJu5ItNx4MCBLe7WTGh07NixXg/yQbhPdna2kQzP+MYbb+j++++XFEYwuH5Hjx61xWXOOnfubMQG8YjVu/HGG40IIYRnzZrlNcOSgxri4uL8+ZYI983OzjaxDLoDzVx77bVeX/bfkiVLHN4kUxYLX1xc7IYrkUfuse+pr+BvEydO9DiYv9mzZzscSliTEOv5558fdVZsgAgCCSSQL6fWoLCw0BlPpzaAPHbsmAkhsupiY2NtWUnA+MlPfiIpdLoNFYnwAO3atbOvRi04PmZeXp6JMq41fPhwa3x8T+7d0NDQ4maX+LaFhYXW7lhq7jl58mQTiVj/yspKIySSTp599llJoVp2cs/5TE1NjSvUyMKEGxkwYIAzziJ7PYDKQAKE2WJiYkzARiuE1NasWWNuBX+ZtaiurnZGKWswdOhQ+7igv29/+9uSQhwBmZPsg23btrlZDU1RQTv9+vWzReRa5513nr/7pz/9ydeVQpad77ZEQF779+8/7UQo1iA5OdmcDjxUfX29LTqc16OPPioplAkJgUjGYNu2bU0Ocl1Ckbfeeqt7GvCZ+Ph4IxTWlNqSlJSUqFFegAgCCSSQ1kUEaMaTJ0/aZydBBDZ448aN1qaEfLKzs62FiSg899xzkkIhHK6Bnzp27Fgz/VhP/LSjR49aY6K9FyxYoHvvvVeSTqsUS09Pt/WKVmB7KysrmzXllOSw1pYtW/w7OIWrr77aXAk8AB18cnJynITDvFxzzTVOMIH9x5oeOnRIV111laRwdGTfvn3mGUAjPGtOTo4RR7SCpaqrqzstcYpoxocffmi/lnGOGzfO3A2W+sknn5QUQhSsMf0FRo8e7epUroFljIuLs9VjPbdu3eooFNEU5icjI8PoryUSyeqz30CNRLBWr17ttQEFTZs2zWgGZElIMz4+3jwICGrGjBn6n//5H0lhBE3noUWLFpnbocZk48aNTrZijiLDqUTnziStqghY/Ly8PGe0AS+B0Glpaf4cE9ymTRt/npedF3zq1Kk+xIQJW7ZsmXMF2ABM0ueff+6Xix75kydPdmiHjUzeQXx8vGFvtMKmOeecc/zcwG4WJiEhwRuDzV1TU2MICtlEnP/iiy82CUSG2ksvveQGHORhMI5Vq1a5kzMNW0aNGmVSlGvgkuXn57e4VRkbtWfPnp5fCFBckNTUVL94KJ2mpiYTmMwtazd06FC7bsTM169f7zZ0wHpIufz8fIclyaDs06ePYTJkIetZV1f3hY48Q8aNG+cDXHmhmYeMjIzTyLm+ffs69M1eZM/feuutPpiG5/z000+dd8DcsC4LFy7UT3/6U0nhvoeTJk0y4Xrqgapt2rTxPjqTBK5BIIEE0rqIAAuYmJjof5+am43FksKZaHfeeafhJdYWy/Dqq68aVpJZ9bOf/UwvvviipHDTD1p43X777YbAJK7Mnz/fcIp6BUi3jh07OtwTrRD+at++vclQEE8kQsHaQ6JlZWUZkYAcmJ9Vq1bpgQcekBTOCpw7d67bU+FmYJ0mTZpklwYi7t1337WlxnJz78j++9EKa9ilSxePBfePMOLbb79tFEd466qrrrK1j6y9kEJtxsiVp3npY489ZsvJ3yBHf/KTn9hC096tvLzc8wHioTryxIkTdiFaIuzPuLg4XxsCGxTwX//1X6c1vUlKStIPfvADSeGQKmN/7733jNo4mWv58uUOpeIuQwyOGzfOiWns3Y8//ti1CBDklKvHxMQ44/NMEiCCQAIJpHURAbJ8+XLXTGOdScO89tprzRFA3M2bN8/EEcQQlq9Xr16+Bgke+/btc5IOSAByrLCw0P4i6GLIkCG2aIQPyclft25di31nCKN169YZkYAq8NkmTpzoMRDqevHFF00C8Tv86p49e5oA5Zq7d++2lSFNmRBVY2OjLR/+cu/evV0lSINQ/NG3337ba9DScW7durVZDbwURjSXXXaZxwnh++ijj7r3AWQqa37xxRf7fAqSu1JTU036gYCQjRs3OtUXqz1kyBATjjR6BVGuXr36C510hOTn5ztECpohnfmBBx7wGrHffvnLX/q54Lw++OADPxP7+uabb5YUWgd4IuaXPbN27VrvI3ilEydOGIFyXYjblStXRs0RtKoiiGxCcmrhBpv1ggsu8IGeQK7+/fufxrCzkW+77TYvOkqie/fuzuKCmKGENzMz01EDCLakpCQTLT/+8Y8lhaHnoEGDTuurdyYB/sfExPiFgLyEQR4xYoRLiCGFzj33XJNuZBiSI/HII4+YpcZt6datm4ky5oVIQb9+/TzHvGTdunXzGgBF6bYzYsSIZqdPt2ScWVlZVrysJwr1wgsv1Lx58ySFC6N69+5tcoxnpJT4nnvuccFO5OnMKHnGicKsra11JyMUTUZGhqE5Lx/kYnp6um6//fYWjTPyfvHx8X4ZgfiQj927dzd0Zy9eccUVzjxkPJDVc+bM8ef425AhQxwRwb1CuU6ZMsUFRhROFRcX2+3ElYY8HDx4sPfdmSRwDQIJJJAv51yD9evXG84AjSLPmgcWYykLCgr8eX6H1bjlllsM3UEVmzdvdqwXiwcK2Lt3rxEERMrJkyd9T9wFIPnhw4ddmkwu+5kETb5582ZDM8gwxlFRUWFNTi7+jh07TObh7kSeEgTsjDzyjJwL5pHn3rRpkwlVIGyki4P15N7l5eXuLIy7cCbhXh999JHnjWeDhIyNjTUUx7JVVFQY/uI2QJBdccUVvgZjHzp0qK0+LboIN9bW1nrvULF5/PhxX4PQJWteW1vr/oqQl9EImX+FhYWO+TOvZPYVFRUZzlNFmpqaetoep0XenDlzTnPfVq5caZKQsCNode/evUa2hKUjQ6GsKe9IbW2tsy6Z3/9NAkQQSCCBtC4iiPT50ZxkwkHgxcbG2l+MtOZoOcIz+JgHDx50o0hy9v/617+afMHicFz4gAEDHJLDyhQUFJiYJASFpj5x4kSLc9MjE23wz7nXf/3Xf0kKWVMsEuGhUaNG2YLhf/P98vJyWwpIvZdeeskhNqw5JFXPnj1t7fEnV65caUSCD41v39TU5LmNViBY09PT/W9IQ0KRZWVlDp/BG/Tt29doAh+e+d62bZu+9rWvSQrXLcyfP/+0sBxI69ChQ+Y2uM+iRYtOSzwjxPZF+i5IYfI2JibGfR7Yu4wrLS3NqBEuaNKkSbbo7DFCf0eOHHGmJ77/yy+/bLTL90AgqampXi/26erVq41WmBvQZFxcXNQHvgaIIJBAAvly+hGUl5fbIuDr4CPl5ubal43sEY+PjTYmQeTjjz+2RiYZ55xzznEnHqINkSwrTDVy6NAhWw5Y/si695YiAtBOTU2Nv8upPViqTp06nWa1PvjgA48dtpfU5IaGBvvH1Gl87WtfMxvOvMDDVFVVncawJyYmGhGcGoZKTU01ColWsMonTpzwmHkeEF56erqtPbzNJ598YrSCpQfZ/OMf/9B//ud/SgpzFYMHD3ZlIeuOZfzXv/7lNcb6VVZWGh3CFTDXnTt3bnGVpRRGaCdOnHAiEfuOyNSRI0c8n3AgL7744mnndoJOVq1a5bRxogbXXXeduSPmC34jLi7OnABIKiEhwQiU+9CqLDExMWpE0KqKgBewurraGWgMiI2/bdu207LUjh075iISoDUQuKioyOE0XrbBgwc7Q5AXhM2Ym5trpQJU69Gjh0NzKAP1ZiYAACAASURBVAJe4IqKiqg7wSIokaqqKm9AYB7kXmFhoTckP5OSknyMO4QWL0pRUZE7P1OiPHz4cL9QxOMhjHJycrwZgZZ9+vTx3J56XPa+ffusqKMVymTr6+u9+VA0uDGRJdyRR5Sznowl8ug2QqB0ou7fv7/X4NQefBMmTPCLwDUGDRrkvAOMCeM9cOBAi8vKpfDLVVdX573HukEW7tmzx2HfyLHiCrA/kfr6er/YGMKhQ4d632MsISUzMzO9H/hd7969fX/mgb2+efPmqHMmAtcgkEACURusRiCBBPJ/rwSIIJBAAgkUQSCBBBIogkACCUSBIggkkEAUKIJAAglEgSIIJJBAFCiCQAIJRIEiCCSQQBQogkACCUStXGtw3333NUmh2gFyscmnRhoaGlzAQs51ZOdYcvfJq+7Ro4dz5CkVLS0tdaEG1+B7MTExzQ5akZofS0WRBt2Hy8vLXXAyd+7cqOpX77nnnibuSe491yX3Oy4uzqXVfKapqcn3JbedJif19fWeKxp8SOH5o7SanPzExESPmYKWwsJCf57fMbYDBw647uPZZ5+NapyPPfZYkxSqbyDvnnFGHiIbWSAjhTo6k+8fWQyEsNY8W+fOnd3cg2twTSncTIZ6jqNHj3pP8DnGu2/fPu+vp556Kup65EceeaSJ8bB3uR9y9OhRPws1Jfv37/dYqQ/ge5GFZ+xFKdxshJ+MpX379v4c81VcXOw5Z96osygpKfE6PPnkk/92rK2qCHgJcnNzXRTExFEc0r9/f5/MwwtSVlbmmnMq8OjUkpGR4c8zEddee62Lb2jbTb+8TZs2+Yx5XsTjx4/7BaI4hcnv1KlT1GfMIyxubm6un4Pr0Wo6NTXVVWv0F2jTpo3HzEvAmGJjY11MgnJo3769O/VQxUlRzrp161zPT3HVoEGDrEQYJ/NYV1d3mlI+k/DcGRkZrkTkRaX4prCw0L9jw3bv3t31+rzgFGpdddVVVuh0HIqNjXWfA6pKUdz79+93wRp7KjEx0S8RihWFkJWV5e5BLRHmq2vXrq4QZb4Ya0JCggvrMETJycmulOTzrG1OTo7njQaoPXr08DwxvxR3nThxwn0ImL8hQ4b4GlyXvRYbGxv1acitqgiwWnv27HFpLCfu0Jt+4cKFrvxjU7/33nuuukLT0oxh2bJlPgaKl/7AgQN+yVEAvAAjR450B10WcNeuXZ78SOQghRYS6xytUKa6Y8cOKxFad9Gwo6SkxFVjLO7ixYt9BBgbmGdcvXq1nxFlMWjQoGYnMknh0uARI0b4gE1e9kjrgVJhbCkpKd5w0Qoo4MCBA543no1y5PLycltAfhYVFVmBYZ0Z9/Lly23RkJycHDeTZd9Quh0fH++x8Axt2rTxPkGZIF+kmlQK74eKigpbdhQya7Br1y53S2Yd1qxZ4+a3vKA0LN29e7cVFs/Utm1bGwjmlz3coUMHKwyqSPfv3+/PoQhObbsXjQQcQSCBBNK6iIBKx/j4+GYHhUpq1kILy45Vv+KKK9zIEn8LeLd161ZDYDTu4cOHjTCwNGjxxMREowOQx7hx4/x3as2p709KSooaXiE8d0NDgzU3sBXLMnDgQFs3YN5FF13kNlM0WQHWl5eXu1EFv6uurrbWB22BdmbPnu2mHMxV5BkDnADECU9JSUlGKNEKbsvhw4e9LkB8rNL48ePdio3rV1VVuQ8B1o5eApWVlXaf2Bv5+flGWaAbrOCUKVN8whUHzFZVVRkl4Esz15Gt61siILTDhw8b9nMP5nTChAneR7gI559/vg81Bfkxb59//rnbkrE/SktLjRJOdUHGjh3rRrqg5bPOOsvPAQoDRaanpzfjk/6dBIggkEAC+XJalaWlpdnq4xuhcevq6qz98XkiT/mhgxA/Bw0apD/96U+Swr75ggUL7IPSFBLWuKKiwsQkFudvf/ubLQZNUWl22qtXr2aMdjSCf5ibm+u2U5BNoJDu3bsbtdC6qqioyJYOUgwfNysryy3KmMctW7a4JRefZ9y7du2y5cMCPf/8880OGYkcZ2VlpX3eaAWE169fPz8TFhFE065dO7dFw4cfM2aM5xskg6WbMGGCz7+M9Ms59QhkCHO/a9eu0w6Pqamp8d5hnCCgDh06GFW0RLD6eXl5Rnz466ChnJwcnzwFV9DU1GQ+A5QAud27d2/vAfZ/fn6+uzuB/Nh/ixYtanZdKXQi86lcAs+1c+dOvxNnkgARBBJIIF8OR1BfX28kEHkMmhQKr8Gs0xq6Q4cO9nvQcFjR+vp6Ww6Y5ZkzZ9pK/OQnP5EU9tu7dOliq8m9Bw0a5MgD2hQNvWvXrhZbEJjwxsZGM/dYJpj5jIwM+3bwESkpKfb58aexBp06dbL/vWLFCs8P1ghriLV59NFHPcc0tezTp4+mTZsmKewzMxfbt29vMZsODzBu3DiHdWkuytw2NDTYx+U05z179thi85O/vfPOO240Gxk+BAHS24+oQHp6utfu/ffflxRi80F2PAfhx23btjWL2UcroMd+/fq5nySWnuvt3r3bYU0iBWvXrvXvQEnM+bZt24zyQEGZmZneqxyJR2PX7t27u4U6ez03N9fjZz/RfzPyxO0zSasqAjZ5eXm5Hx4YzaK/++67nhyIs/Xr1xvaQ+Zdf/31kqRnn33WZ9kBQbOystwc81vf+pak8MtZV1fns+GAsampqV5oXBAUT0ZGhp8nWuHlraio8DiBb5EvLPkMkE8fffSRNwljoZHnk08+6ZeMBqcDBw70sdo//OEPJYVJp7KyMrsLKKGysjIrBTbepZde6ueGjIxWUMrLly/3hmONOe/v+uuv93xACP7rX/8y2cXzMgfdunXzGrMml19+uZvWQiTS4XjHjh1asmSJpPDeyMzMtOvJvrnppps8B8Drlgjh6vz8fK8pe+Rvf/ubx4DbiUu6Zs0arylGAHeuvr7eL29k09df/vKXkuRzLZnnbdu2mQwmP+XIkSPes7xLvBvJyckOz59JAtcgkEACaV1EgIWfMmWKtTinDwOfli5d6nAX8O7iiy82qXJq9lteXp5eeOEFSWFItH37dltKYD0hnHHjxhkeA/G6detm94LEJUi03r17G5JHK7gD48ePN1THgkGqrV692vfEDZg5c6bvy7Oh3VNTU036MVfp6ek+ORd4CuIYMGDAaWfopaenO1uNk5d41j59+pzW4vxMgsXOzs72ukCGAsVLSkqcIATRd8011/hMAJ6buaivr/f6AH+Li4sdImbsf//73yWFLChhQ45Aj42N9XyzlyLdhi8iWN1Ro0YZaTFWLH55ebldSojaW265xRA/cl9IIWRAeI89UFJS4rMRQVkgjnPPPdct7RlX9+7d3bKfvQXi7dWrlxHgmSRABIEEEkjrIgIIPPw3SXrllVckhXPU77jjDlswNFtjY6PJKE6L+fOf/ywppI25LmcmXnLJJU7KePbZZyXJ/vjWrVudeAGPsGHDBpNnaFfQSF1dnZFMtEIS04YNG2w1QQaEvc4++2znoL/44ouSQmf3YTW/8Y1vSAr7/ueff75DaJz1GFmgdeqJR4sWLbKF4CzB7du3m7DFAvGZtWvXOg06WoH8KisrMykHt4Glv/nmm/XWW29JUjPi9IMPPpAk3XnnnZKkn//85x4baBHSeOrUqba+L7/8siT5zMCDBw+aTLv//vslhawlVhuBgKuoqDCqaIlA6B48eNDIjNAw/EZeXp65Lgjd0tJSE5wc2AKCveqqq7zvQCx5eXneb4sWLZIURpOHDh3yPN16663+HfMKygMt1NTUeI3OJK2qCCKz+4CLECGcxlNZWekJhuAZMGCAyZq3335bUlhx9OzZ0zHiO+64Q1II/kGcQDIBwVavXu3rn3vuuZJCBBD35yXDFTl27FiLMwtZiMgsSUgkCLPGxkbD9B/96EeSQoQiCuDpp5+WJE2fPt3P8dOf/rTZMz788MNWligurr9x40ZnFkLWde7c2WQTJBKKICkpyXMWrbCJIwtxIOL4/x//+MfTCMGUlBT9+te/lhReT+Bwenq6oTSuSmNjozPwIP8iT4fiusDm1atX+9nI5oPE3LNnj/dhS4Q906FDBxsZjAtKvqyszHMIITh16lRHv1DIKOH58+fblSMCkZqa6jWfM2eOpPDeXbNmjd0FDExxcbGNAUV9KPSNGzdG7dYGrkEggQTSuoiAqquYmBhnWWFtgVsLFy40+UOl3Msvv2ytjxuAVl+/fr2RAyGrqqoqk25oa7TmxIkTfYQ4FnPTpk265JJLJEm/+MUvJIW1fWSJcrSCBc7Pz3dGGsgHS/nWW2+Z6MOqHz9+3EdbYyEJQ8XHx9uVee655yRJ3/ve90xinXqtGTNmOMSElSooKDAK4hw+5vXEiRNen2gFiL1z505bqsh+CFIIHZ133nmSwvkMixcvtltEPggu0YABA4yKyDbMyMjQH/7wB0lhgo5s0scff9xhw8iSZs4bBF0w7nbt2umLnO7F3ETWAnA//vbPf/5TY8eOlRTOmHz//fcdNsTSsy6HDh3S9773PUnSa6+9Jim0Z7D2EI64GY899pgRFPNdUlJitMmeJ7yZlJTkcOuZJEAEgQQSSOsiAuTEiRPWaGhTCI8ZM2a4vptQzLBhw+xrk8QBMfjXv/7VvhHX6Nixo8NXZKDhi61evdoJQhBAx44dMwLAauHrrly50mHJaAViKiYmplmGYuT1p0yZ4go8fMjS0lJbQUJSJPwsX77cJBIW5eyzz3YSEIjqggsukBTq04AfjVX45JNPmtX9S+FwbEJCwhfOoOzSpYvHyXOAtgYMGGDUcuWVV0oKoQQQAdaV8WZmZhpVEA588MEHdd9990kKJ/Z8/etf93OAKuADli5d6kxExkSNf0NDQ4vXM1LatWtnvoHc/j/+8Y+SmhN9EHxbt271erHf+P5FF11kMg908cQTT+i3v/2tJHmfQgAXFBSYbIY43rRpkxEAXAKIIjMzM+pwaYAIAgkkkNZFBGjlAQMGONzF70jkKSgosH8Fq378+HGz4iSSYNkyMjLMwsIa19TU2KqAFvCzLrnkEvMMjz76qKQQHwC/QNgLy7x9+3ZzFdEKiGDAgAF64403JOm0uoJt27bZn8TvmzRpkpNN+B4t2jp06GDLAHopKysz94DfS8uyYcOG2RqSqtq3b1+H7ZgrENPq1asdQYhW4D8yMjIc6gNhgPji4uLM9M+fP19SyH8mCgBXQfhs7NixDhHDFSxatMiWkMQi9kFcXJw5ENLJu3Xr9r9a4fr6ekeZ4KKiEa43ePBgPzNJX4yvd+/eZv9JhurZs6fTqVkb0FNsbKz3AOu+ZMkSoyr6OPCO9OzZ0+v1s5/9TFIIyRHhgj9hv6alpUXdj6BVFQEwKC0tTVOmTJEUJv3Y5L1791Z+fr6kkJsghUJKxIUh4oCeJ0+edI4Bk7po0SJdd911/6/3jomJ8YQ9+OCDkkIEFwt3arZfXl6eYXS0wstZVVVlpYNyYJx9+vQxyQVkTk5O9nMAMYH6SUlJLvIB2j799NPOMyDmzMYvKSlxViJk6vr16+2KEauH1Ovbt2/UWWhIZFYgkJ1NDGzu06ePlTAwtaGhwUoKRY171KdPH4fqCHFmZmY6Rk6Yl3k5efKk53Tu3LmSpN///vd+IVFuhFWzsrLsQrZEmKfY2FjfD+XLenft2tUuIPfftGmTFQEvKGM/fvy4lQP7rb6+3m4tYVfWtLKy0nsc4vj555/3HsP44XZ16dLFOQxnksA1CCSQQFoXEWAVlyxZYm0K5MdqHD582NaQ0OIrr7xi0gxNCxnU1NRkiEoGW79+/dy+CthIOXJBQYGefPJJSWGIfeTIEbsaQECsRkNDg7PmohU0cn19vUM7p2YAtmvXzvOBhXj11VcdPiTUhBX58MMPHXpE85977rnOwgTJYJ3i4uIMralarK6uPi2cyVzX19c7KzBaYUzl5eUmJiH/IKw+/fRTk4W4A88884zdPtAQz/PEE08YCVIhOXHiRM8blhNUt2rVKj83e0LSaQ09aRhSVlZm9+ixxx6Leqzso82bN3uMIBzuv3fvXv8bEvejjz6yW8I6ExZcuHChoTtzNHv2bGfbch/chrZt2zrcSB1JcnKyv4tbRtJYTEyM9+KZJEAEgQQSyJeTYnz48GFrux/84AeSwlY08sCSe++9V1LIOmMpSZkk0aZ9+/auzvrud78rKURUkeKJ1SJho66uzhqZkGKHDh3sj0a2G5dCFggfLVrhGePj481twFng48bFxXnMVEoWFxfbkoF8IkNBWDX85TZt2tiiULEHkdW3b1+ncYO+Dh48qEceeUSSXP2HZT106FCLw2oglLS0NPMdhCrxb/v162dyDr++rKzMhDBoi+/fcMMNJjdJsa2trW3W1EYK968oLS01imNPFRUVGVVgXSFr09PTzau0RFjTwYMH+94Qd3AAO3fuNBEIZ3LZZZd5L5JEx54cP368Q8hY+E2bNnl/ntrEtG3btkYhILuioiInwy1YsEBSGEn36NHDXNyZpM0XybL6onLvvfc2SSHChY3CJmHxly1b5s0P8dSnTx8TZeTxA5mzsrI8wXx+2bJlLkoBlrF4lZWVp/X+i42NdeYZLgpQsH379oaX0Z4AxDirqqq8mFwXAq+goMCbk82dnJxsUpNcCkiqoUOH+qWhK9HWrVv9sgP/gb3Lly/3mPheVlaWm5pQtIWb0b17d7+cv/71r6MaJ6f/FBUVeYPi7nDdEydOeGOywcePH+9OUGRCAqWHDRvmCALfe/nll11nQR4JBHFjY6PdIsjOyA5VuE4ozA4dOpjlf+6556Ju/P/AAw80SSHlfuqhLLita9as8V6J7IGJuxBJWEqhvAf2LvM3f/58ryXK7He/+52kkAHFNSKv5vzzz7ehQpniRvXr18+G5emnn/63Yw1cg0ACCaR1XQO0ZM+ePa3FsdzkY8fHxxsGAlvXrl1rKAThhMXeu3ev48FYyiuvvNKWBotK2LFr167+HGWr3/72t22xgd0giLq6Ouc4RCugl2HDhtkVoBaAcNHUqVONboCK//rXv6zxcVV47ry8PFtIrN38+fNdS4FlASF84xvfsHuBW3L11Vc77Er+PzHq+vp6W49oBUg+YMAAPyfXgwROTEx0HQHrunPnTodkyZAjJn/ttdeawKSK7tJLL/V1gdJAcCkcXnvmmWckhcp8uQZuFy5C27ZtjXxaIuy3YcOGeY1AP5HnN7LHQadbtmxx1uSp7lBTU5OzIRlrTU2NXWjmhDZmw4cPd14J2Yy33nqrXQeQCUilW7duRihnkgARBBJIIK2LCLDqu3fvtjXBSmCR+/fvb9+ZHOr8/HwjAXxDMssGDhxoq48mzMjIMIFI+A6LNXr0aIcZ8VnbtGnjfHVIRcibxsbGFmfckQW3YcMGPxPWFv9y7dq19h2xBv3793fGH6QfJODtt9+u2bNnNxtnTU2Nw5xwLsiNN97ozsBY386dO9sXhcTEX66trXUSULQCT5Ofn2+UBf+CxTr77LON7Kh0bGpq8nPg69Kg5Je//KXDgKCKyB4CjJO90bt3bxOgkKmZmZlGTy+99JKkcNixrKysxchHCiOQ9evX28pCErInhw0bZuvMmpaVlTlUCsqEIL/nnnscNqVaMicnx74+nALEY/fu3Z1RCG8wYsQIzyHokE7HkVm6Z5IAEQQSSCCtiwiwyvX19a7uw/Ii9fX1/hwhwiuvvNIaGf+YtNk5c+ZYY8Ioz5o1y1oVq0/4ccuWLa4Miwyr4cfhg+ETdu3a1X51tEKe/cmTJx0FwAqRSrtmzRpbPKxI9+7dnRTCTyrwLr/8cvMG+KZ5eXlOIYV1x6p//vnnTlVlLo4dO2Z/mnuTJjxy5EjPd7QSGXXBssH90E/h2LFjRh+ggLKyMocqSYahx0JOTs5pLd0vueQSt/liHvGtKyoq/N27775bUmhP0cUJHgarOXz48BYniElhP/3IkSMeBwiH2ojFixcbgRKlSE9PN4Ljb3AZKSkpjuyAGnr16mXUiESiH56daMjJkyeNJkAqpDlHVkOeSVpVEfCg2dnZDnHgLlBf0L9/f38O4isrK8vhrlMPCe3Vq5dLUm+77TZJoSKeyy67TFJ4Y5Jbv2DBAsNQ8vpzcnK86VAchLfi4+OjLtxAcA1iY2PtAgFbyR2IDCvh2qSkpNjl4XOcSTBq1ChvIEihf/zjH96EKErCdmvWrHEtxUMPPSQp5GbwUuIKkeu/du1a/zta4eVoamoyjIX8Iw8DElMKuyiXXHKJoS0GAUJ2woQJbmOGmzRr1izn1gO5KbRZs2aNoTaZohkZGQ5H4/owtuTkZBOPLRHCwNnZ2Q7t8oKzhzt16mTFw7rFx8dbweJKQeKOGTPmtMKlhQsX2mhF5nhIocxTzvBAgaSmptpQQVDys7q62kbmTBK4BoEEEkjrIgIgc48ePYwASKYB3v/iF79waAsyZtSoUXYXyAYElu3Zs8ewD9LoqaeesmXCemIxr776asN+oOSTTz5p5IBFJQd+z549UR8kiYBkOnXqZHIL0gZ4+Prrr9sKMgfnnXeek0mwNliy999/X7/61a8kSfPmzZMUyst/6qmnJIXJJmDh9OnTTR7R1HXx4sUm0U49dSc9Pb2Z9W6JZGZmOgTKPYGwMTExnm/Wc9iwYfr2t78tKRxSZn1fe+0114JQGn7PPfd4jggL89x9+/Y1GoFIe/DBB+1ycn0s7uHDh22tWyLcPyUlxXsPCM+zHzx40KQmVZ733nuvUQ/uHq7aokWL9PDDD0sK1z3cdtttJoBBjFx/5syZDk2z3h9++KHHxt/I4Ny4ceNp54D8bxIggkACCeTLCR+uXLnSiTs0q8BS/uY3v7HGxDe64YYb7AcRBsJSzp492xaEWu1IIg4ugXDcxRdf7N74kck9IALCjmjZTz75JOqkDATNvHv3bof8Fi5cKCnsJ86YMeM0/+2ll16ypeb+fH748OG2FCCmNWvWOB2btNzIWnT4FHztDh06eP4YJ1zBli1bvD7RCtamsbHRfi98ChZ0wIABRgugsp/+9Ke2plhsSODVq1fb/4e43bp1q9vSw/3QzPTGG290khHp6seOHfM9IRI56Wrv3r3NqhSjFXicHTt2eD6Ze9Db+eefb0TJ3v3qV79qFAvZy5rGx8e7AQ48V01NjTkIkEdkKJkENeoKIslVeBfepaKioqgRwZfSs1AKk4OQH7CmkQ/PRM+ePduTyALTXeiOO+5wFIBNlZqa6mw6IgNcs23btj6pFvgaFxdnt4UCDmDplClTWlx0FNmwg03NBgbGpqWlmbiLzC7j8ygyagN+9atfWakBbfv27evaBTYNMe0rr7zS+RhszjZt2jjeT6yZ/I3Ro0dbKUQrkFLHjx+3AkBJocwnT55sxUuG4U033WR3AYUAfH7ttdes5FEugwcPtrKHiAV6z5kzx+W9FIxddtllVojkjEQSppRnt0R43sTERD8zewVFNHHiRL+MKMKzzz7b+5J9QSel73//+x4P6zJkyBC7pShtFMdXvvIVR01QNOPHj/e7A5lMpGnIkCFejzNJ4BoEEkggrVt9+NhjjzVJIfiHlSImCxS+4YYbrL3RlgMHDrRFh4whHPejH/3IBBIWnvJQKayFsViDBw92iJBegSNGjGhWnRZ5rbS0ND/HH/7wh6iq1e6///4mKURMcV2ux/XvuusuH3/N38466yxbcawisPLmm2/2v/l8u3btTMDhekCi9erVy5aScebk5PgaEFCQmRdddJFdoGeeeSaqcT7++ONNUgi6cx3KkEFwTU1NDqnhMo0cOdLrh0sGvL7pppvsskFenn322c1CYlI4tFZcXGxECMKKzNdnvlnDvLw8h3R/85vfRF19+PDDDzfp/2nvTIOrLK84fsJ6WRNCCFlYEhOS3CQQwiZMYSR2itQixa2yDNo6bWmVjkopKK0dpVhHUceWqu1gF21RbMepjgWKAmIREBwKZUlISEhCWLInQAKBJKQf7vz+9w2Zyr39kPbDc75kufe+932W95z/+Z/lsUCZMC4OZC/7KSkpSeXd7NeMjAwhW9w+5uO+++7TfeLWTJo0SZ/lf6DDXr16CfnxuatXr8rVAVXg4o0ZM0bP2UsvveSqD504cfLF0q0cAf59SkqKLDVdacnD3rlzp4gTCJHRo0eL+IIQJKGioKBAIUL8srfeest++MMfmlmwGzHWZdiwYbIuJNxs3LhRPiB+Mj5rU1OTMvlCFazW6NGjpcG5X9pvtba2isfAt50xY4bmBXIUpGQWTNYhrPTpp5+qth1/Gkubm5sr5IBFOXnypBAYn2Oczc3NYZ8A5O1zgB9LNiPtsqKiojQG7/HdZECC3vjuN954Q+FO0MLGjRvFsRDKZe2WL18uCwgaWrNmjZKLQEzcw5EjR/6rWgPvWZv0uCBMBxK5dOmSOBvWNDo6ustx9oTKKyoqtAdoR/bJJ5+IvwFxcGjq4sWLu5zh8NFHHynZ6XqOp7KyUnN+I3GIwIkTJ93LESxfvrzDLGC50WL8JP303nvvVVgNi7Nnzx5ZECwa2vjDDz+UReC1qVOnym8iDRaLX1VVpe/EF/OeSESIDlY6KipKbP1jjz0Wkk/56KOPdpgFWHV8fuYZaz516lRZGf539OhR/Q9Lxs/9+/cL8ZB0FB0d3amfgFkwQlBeXi7flWvW1dWpOpCoDdK3b1+9L9TOPXTt6dOnj1AQa+Ft9Q0SxCIWFhZq7rFi3pAekQzY9draWjH1oDM4lIiICPELNKAtKysT4878M7bKykqhkLVr14bMEaxZs6bDLLBW3ipKs2A0ZO7cuaq/YD5OnTolBMJexEofP35c6AIL37t3b0WWiFaxxpWVleI6mL/m5mahQd5HlOLatWua3xUrVnzhWLvVNfD28uNmgaYMxns0Nw9gYmKiICfhNR7YpqYmhXE4SnzIkCG6PmQRfoRidAAAIABJREFUD39qaqo2E5skKSlJi8oicR+FhYVh5+ATuuJB9F6XTdTQ0CDCx5u5eNddd5mZqZiIB8B7SCnhuOzsbBFEvMZGyczMFPkHWeX3++UmoDQJ8504cSLsI88IW/Xr108EHMVPzHGPHj0Ey1F4cXFxKhD7wx/+0On+09PTbenSpWZmKjQaOXKkxoUyYV0HDhyocBsFTGPHjtV+AqKjCDo6OjqtS6jCvdfW1krZkUFKXsHZs2e1Z9l/jY2Ndt9995lZsMTd2+kZ5UW4euLEiVqv6/Npxo8frzVlX/Tq1UvELHsWN62xsVFK+EbiXAMnTpx0r2vgxImT/09xiMCJEydOEThx4sQpAidOnJhTBE6cODGnCJw4cWJOEThx4sScInDixIk5ReDEiRNzisCJEyfWzbUGy5Yt6zAL1AeQ+01BhrdfHoUb5JM3Nzd3OmDCLNgVuEePHnqNUmOfz6ciInLYvc08yFunMKW0tFT3w/vJ1z5z5kzYDTtWrFjRYRYoPOE+uD7FQVeuXFGZLfnzFy9e1P/IVff2t2Oc5L3369dPY6GGgZz//v37K6ed3Prq6mrVVHAtPuc9u+FGTSyQBx54oMMsUFfAd1FEwzja29u1tqzZpUuXtO6ME4mMjNT/WOvz589rL3C/5OP7fD4V4DBXNTU1+h/jZI5ramqUf//222+HXHT0k5/8pMMssE9ZS0rGWdP29naNn3tpb29X3QU/vceQcZ/UbbS2tuqe2TvMh7d8mn1aW1urMmfuizqSqqoqjfX555///yk6YkB+v1/dWrhRTr4ZNGiQiizY1PHx8Zo8io5YhNjYWF2DSU1MTNT16BhDJVdZWZn6uNEDISMjQ9fjQaF4pLa2Vr+HKiid5ORkFYdwXe4rMjJSPRYoHDILVq1dfzbk4MGDuxRSRUZGal4odKK4af/+/SrGorIvNTVVDxCbh04/cXFxYTcv5X6SkpJUwIXSpIGm99APlMXAgQP1vdTy8/fQoUN1LebC26eBcbLZ//Wvf+lEbar20tLS9GBxP+yNqKgoXSsc4V5Gjhyp4jnWgb4B3uIrlMPgwYP1P+aeecvMzNTvXHPw4MGqrOVgFNY0Pz9fPRcwBgkJCVI+1xcYRUdHd1G0/0n+J0eeFRQU6GHhAaVTa3V1taqpqCDbs2ePHl4GS9fbHTt2aBOxOYYPH65NQeUbG2LEiBFq18VrJSUlqrxjUnlIm5qaOlVEhiJYn7KyMm0C0Adt1c6ePavxIUePHlXjFR4eOtQeO3asSyPWuLg43SfjY8Omp6drnFiWwsJC3QcPPQq5vb1dD0+owrxXVFRonFQ3Ms7KykqVUvP+srIyKcTrz3soLCzU/DGmAQMGqNoUa8nDnJWVpbJl1r+8vFxzBLr0NhAFaYQj7N3i4mIpNOaVczXOnTunxrIoh0OHDqk6EaPAGm/btk3jQamkp6ereQ1Vr14lRsUqc1lfX699DDpEWV67di3kpjqOI3DixEn3IgI0lheCYvl4bdq0aYKGvDZ9+nRBSGr34QPq6+uFILAMHR0dXVqiA4/nzp0rd8F7HDmWiXMW+DsnJyfssw+xFE1NTfKVgeLcV15ensYHypk2bZpOCrq+SWVVVZVam9P3fuDAgUIJXAPLN3PmTJ0FgLUdO3as5p7++CAgv98fdgsvLOPVq1fFQ2D1sOazZ8/udNaCWcCycSITbhG190VFRWrbxdgrKir0OyiKPZKRkaG9wRynpaXJmnKGJQ06Ro8eLdQUjrA/zYK+OnMOHxAXF6f9xmt+v1/nWXoP4jULIEdgP2eB9ujRQ+iUtcSqJyQkaO05F6Rnz55CrMwz+y8+Pr7TfX+ROETgxImT7kUEECjJycnS2LQlo31UZGSktCN+VnFxsd4PqYJFGTNmjKwo1y8uLlanHLgItHhBQYG0MNbxgw8+kAVGG2OtGxoa1E0nVPFqcO4bC0bjyp49e3biIcwC/jU+Lb474/T7/Wrnhj+5f/9+NUXFGmCRdu7cKcQDm7xz584u7bLw5a9du9aJzQ5FsETR0dFdTv2he09jY6OsMq/5/X6dlswasIZZWVnid1izDz/8UKiPvcG9etuSgW42bdrU6YAR7+fOnTv3X3UoYl2GDx+uNQWlgAgSEhJE8LFueXl5mgs4GE6lmjZtmpAQ61JTU6PrgaBApD169NA8cD/FxcVaX/YwXZmam5t1jRuJQwROnDjpXkSAVYyOjtbhDVhgQoYpKSnyjfB10tLSujSAhJ3u6OgQY80hHzNmzJDGxB/DN58+fbrQBwdp+P1+MblwD/iiZ8+eDdtS4oOOGjVKzD3cBlaxX79+ui5avk+fPkIyWC3CReXl5Xo//QxTU1NlZbZv325mQZ97wYIFshRYp7i4ODH4oBBO1a2trdW9hSpY88TERI2T9UGam5uFkBhbQkKCQpsgQnz4mpoa8SpY8aSkJL2ONWU+c3JyxMqDmDIyMoQm2QfwDkVFRWLcwxG4mGHDhqk3IvfAPFy4cEEMPrxMXFycwoG8jzWoqKhQpAPfPzIyUuiOQ2P47uzsbI2LuRkyZIgOlYGfYU1PnDgRMiLoVkUALK6rqxNspTsxp/FERUWpoSMP7z/+8Q8pDMJGPKiFhYUiiWbNmmVmATjIuQarVq0ys+CpMWfPntWDAaHT1tamjcxm+upXv2pmATjGAxWqeN0L7o0HFOg4atQo/Q9FVltbKwUAZKS5a1tbmzYIimzcuHE6Feqxxx4zs6Cyraur03UZZ3R0tDru4pY89NBDulfWJFThXsvLywV/GRME3pgxYzptfH6i5CGzOMb88OHDcsV4+OPi4nSYKa4QboBZ8CwHlErPnj3lWnEfdH4eNmyY9mE4wh6oqKhQeJN1wACNGzdOhodw43vvvac9jpLkc3379pV7hTGYNGmS1vT+++83s2BT1gMHDihUiSJPTU3V/zBihNpbW1tDdoOca+DEiZPuRQRorPHjx0uLYQmA+keOHBGRBTKYPXu2Tv4hRMjpRocOHRJ5BkE0atQoe/zxx82scxKTWSBUSOiFVtr9+vWT5SDEA/yeMWNGF7h7I8HN8YbrSJzCUm3fvr0LTJ89e7bCl2SO0e++pqZGSIP3x8bG2pIlS8wsSJ5xOtSdd96pMwHXr1+vcYKycIVooz158mTB6FAFS5Wbm6v55aRp3LsDBw7IIrKu6enptm3bNjMLohWSfIYOHarfCU+OHz9e7gI/QZALFy6Uy/HOO++YWedzM0AQoIX4+Hjtk3AEdDVy5EjNP6QeCGPr1q1dWrffeuutQizX74X6+nrBevZFYmKifeMb3zCzIBrcsmWLmQUsPSjhlVdeMbMAMQ264jlgD3AyVijiEIETJ066FxFg9cvKyuTjcTYhmnTgwIHymyDzZs+eLR8fi7NixQozC2g9QkTf/va3zSyQaor2ffPNN80saFn/9re/ydpjTauqqqTdIWOQzz77rNPpyqEI/EVJSYmsDzUS+GyzZ8+WpQCNZGZmihvgrD80f15enqzcH//4RzMLWFNIVN4Hitq3b58SihhnY2OjwnxYUYjNwsLCLinPNxL82rNnz2q+f/vb35pZEIndc889Ch8y771797YDBw6YmenwD8Y0bdo0fXbdunVmFkjPJmQHqTZx4kQzM3v99ddF/nFmZl1dneaFz3kTlwizhSOkDjc3N4un2rlzp5kFU5snT54sgo+ErdTUVKHYBx54wMzMfvnLX5pZgKSG7OXQmtbWVpGQzCXcTVVVla1cudLMAvvHLMCV8J3wICC1w4cPC3XeSLpVERAPnTBhgjY/bC5kXWxsrOLIPLw+n8/uvvtuMzP705/+ZGZBxdHY2KhJ5JSgxYsXixQDGrIhxowZo9g58LWqqkquB5POQxcfHx92kQrx5ZSUFOUjQKbhZhQXF+vhwT2KiYmRS/PUU0+ZWXDBL126pIeHhzgzM1MFUWx0iLazZ8/quxjL0aNHdR8w0cDJ9vZ2bd5wx5mYmCi4TG0ErzU0NGju2ai5ubna7L/73e/MLKhUKisrpTh4f05OjsbAXAHVMzMztZdQ2Pn5+WLQf/CDH5hZkGVvbW3VmMMRIiqJiYkibVEOMPPeeg3g/+XLl+2nP/2pmZn99a9/NTOz73znO2YWiPf/4he/MLOgAcrOztYaeQlRs4CRxDhCsh46dEjGhX2KizdkyJCQ62Sca+DEiZPuRQRYgV27dkkrA8mBd3//+98FF9H65eXlst4cdw2kPXHihOASUPjJJ59UuAfEQbbVvHnzrKioyMyC4cmjR48KahKPpzruyJEjei1UASoWFRUpfAgcJaa+ZcsWkUIQWfn5+YLuVFdiWa9cuaIQIeM9cuRIJ1LKLHj+4+LFiwWxsTA+n09WjPx3vufMmTNhQ2bGWVNTI0QC0Qc0//zzz4XKGNtHH32k9WRuQSjFxcUK/b722mtmFggPQqaBhkBHDz74oNxGUOXFixeFJkAcxPUvXLigNQlH2K+HDh0Sacu9AMULCgqEyNjPx44dk3vIAbTvv/++mQXcxHfffdfMgki3uLjYNm/ebGZBRIxrcfvttwv1sI779u0TUUwFLyThwYMHQx6rQwROnDj532UWIiTwYC3y8vJEIBLiSk1NVUiEz2LloqKi9FmSdZ566ikhgOszvT777DP51fhxaWlp4iAgqEAjubm5CnuFKvhvw4cPl4XkHmmGMnPmTPm2X//61zU/WBfGh+YvLS2VD4/lW7lypQgrUAWZiG1tbfJXmccdO3aIEIS443vMLGRi6fpxxsTE6LtABqCunJwcJS9RE5CWlma7du3q9J38jIyMlB/MXC1cuFBrhv9LmHfr1q1CH5yK/PHHH4tAhivCx4+JiZElD0eopvQ2NgGlsLZf+tKXRACDKEeNGqVxMDfwHL169dK6QQy++uqrXZqVgIJPnz6t74KXmDBhgsZKKJYw/ejRo0POonSIwIkTJ92LCGAwJ06cKD8J34t88erqajH3JNfccccd8vkIk82bN8/MrFNXHTTjjh07xD2Q3ANrPH/+fLG+zzzzjJkFrMTbb79tZsH0YPiD5uZmhWdCFVjy9PR0WXEsCtcfMGCA0AooZNy4cWLdYZix8P3791f4df78+WYW4FO4LpYetNDa2qqEFNKIs7KylIgDSoCDSEhIENMeqoDw+vbtq3HiD8Mf9OrVS5aQNcjOztZ6UzsA6oqIiOhSt7979+4unan+/Oc/m1nAWsKuEz5OT0/XeoIEeU9tbW3INfpewTonJyfLF7++LuTKlSsK37K/Z8yYob1EYhBj9/l8SkZ69NFHzSzAE+HX82zAh+Tm5gpVkTzVv39/Rc1ITuI569u3r1DLjaRbFQFkVFVVlTY4ITTyygcMGKAHlVh6VlaW2jchQMSoqCgRfGz8iooKbSYeKIizEydOKJ779NNPm1lgU0HkAM3Z0C0tLXJVQhUW8Pz583JlyIiDxExMTFQYk7no3bu3ssRYTG9GHYoROB8bG6tNzWZBuVy9elUhOcJXGzZsEAHFOMn/HzRokOB8qELIKzY2VtmaKF7WetSoUXJfUGAdHR0Kq17vOrW1tekhnjNnjpkFcgdQALgU/H369Gk1bCEs98EHH0jhotx4WCMiIkSUhiPsH7Mg6ccasR69evWyTZs2mVlwL/bu3VtryF5nrElJSSICcVMLCgrkDqK0qW1oaGiQQcQQPvfcc/b973/fzILheZTW5cuXtb43EucaOHHipHsRAZrq/PnzIlP4H0SfWTDRB63/s5/9TJYVUgm4uWXLFoWgIN/i4+Nt48aNZhYs5QR6nTt3zu69914zC5I2165dE2xD82NJYmNjwy7PxTW4ePGiLBH3i9a+evWqrCfhnmeffVZIZubMmWYWhNiffvqpICXhspEjRypjkdAs7lF+fr7mgBBjTU2NMhch37xzlp+fH9Y4cQ1Onz6tcbJO3GNdXZ2ajmBJ33zzTaGV22+/3cw6t57DNQRJDBkyRPuDay1fvtzMAtWhP//5zztd4/Tp050avJoFkWFycnLY1aRmwfqOCxcuCO2A3oDf1dXVIvFAM+vWrdNYQWPemgCIQMhbn88nt+L6EPjx48ftySefNLPg/jxz5owQsbf9u1kgwShUlOcQgRMnTroXEdCeasiQISKXqB3wtmCmXwAhva1bt4oIwXeHqElJSRHZhqVva2uT5cP/JoTTq1cvWVZvw034ApJYSM8tKiqSJg9VsPp+v1/WGCsH2vH5fPLvb7vtNjMLWH2aWBBWwq/0+/32wgsvmJnZt771LTMLWE8SlLCAcAV9+vQRscR7mpqa7MEHHzQzs7/85S9mFrQ6Fy9elN8ZqmCVYmJiZI0XLlzY6TsvXrwof5k53rt3rzih68Nh/fr1U1UoxJtZEH1gHfGtvQ1AmYOSkhJ78cUXzczsrbfeMrMgWVtWVhb2epoF92d0dLTGiqX3HlzCfTK+UaNG6XfSpAk/5uXliegD4eTn58uvZ1/Ai0RERAjhwBu0tbUJJYMMQAHDhw9XX40bSbcqAiauvr5ei8ZCQYLU1dUJ2ixbtszMAhuNwfFgARXT09NVbMLDW1paquYMEI4QRKWlpXoouUZmZqYUBYuKIsnMzAw7Ew3ip6WlRaQV8WUU2MmTJ/WgPvLII2YWiLmjBIGKxMizs7Pty1/+spkFN/yuXbs0Tn6Sb1FbWysIS8nvtGnTBNmBkeRbTJ8+PexuzTDybW1tmvuXXnrJzIJNNfbt2yflDUl70003KQpAXQkP2vz585WJyIOzefNmQWjmDJemtbVVpOWGDRvMLNDhl9d5aHGxIiIiNMfhiLeMHPeRucTN3bdvnzIk165da2YBQ8TYqKFgXXJzc/Vgs78rKirkLvG8sOd9Pp8UN/vZe+AKa8p8+3y+kMfqXAMnTpz8b841yM7OFlRD+2MZpk6dqkpArOn69evlJqDZ0cLnz5+X5YAo27Bhg64PxMZifeUrX1E8nuYmS5cuVYYjxCMWKyoqKmyyEM2ckpIii44V8VZWoukZ044dO2QhCMdhpdPT0wWxIZtiYmJkgYDK5FXk5OTInfrud79rZoH6A0KWVGriekRGRnY5MutGgpuTnJys3yHJcP1uvvlmjcHblRgrjisEyunfv7/6GbKuEydO1H1CuuJKDB48WMQnlZtr165VPgVIgnH36dMn7AxKs6ALc9NNNylEyz2Q0/C1r31NVh+r/Mknn+h95Ijw/QMGDNAeZG07OjqESmnoQk7MgAEDVMvAHL377rsKM+L2Uardt29fkZw3EocInDhx0r2IAK167do1WUPyw0kwaWpqUlgNyzdlyhRlYKH98NmeeOIJJZJ4T1IixxzfHHQxa9YsNTQl0y0jI0PVjOS5e8lJQnPhSnl5ubQ7xBdkXnV1tbL68AUzMzM1ZpKYaCry6quvyrJgDa5cuSILRHIL6CIjI8OeffZZMwvyDYMHDxbJhD/NvLa0tHSqOwhFmO/S0lLVZuD/kn2Xm5sr8ovvGj16tCwbYVtI2kWLFgklUmnX3NwsxMM6wfNMmDBBZDFcTu/evUVM4yPz3T6fL+yWbGbB6siamhr58/jyoM/t27cLCWDNs7OzRQ6yJ9lb69atU0Yo9zd06FDxGqtXrzazYI+JnJwc+9WvfmVmQb5h0KBB4mrgDdivTU1N4lRuJA4ROHHi5H/Tj6CpqUmWC02Lr7h7925Ze7Se3++XD0moiJrtW265RRoZTZ+Xl9elrTQhw6KiIoVl0Lx79+4VqsAfQ0aOHBl26i18RF1dncbMvWHNCwsLNXbCcBkZGRoLfiT+Z3Z2tqwtiGnKlCkKVzFOogf5+fkaE3NlZva9733PzII1CdxXTk5O2DUVoIvGxkZxK9w/SO/AgQPyU+EK5syZI58droCOTLfeequsGIhj8eLFWivSb/G3Dx06pLAx919cXKwQK8iTNF+fz6f5C0e8rc/YuyAQQnSHDx8Wn0RourGxsdNJSGbBRKnY2FiNg/tcsGCBehSAAOHDCgsLxe2AdAoKChSy9bZcNwvwb6FGDbpVEQCTvXnthK/Ifff7/fqdh3/o0KF6ICD/gIaTJ09WUwdizO+//742IsUmLP6WLVsUu4WAXLRokQgWlBChITMLu1UZpNvo0aMFB9nUQOjz588LiuMODB06VMUkbB6y15KTkwX9gJ27du0SjGZTkmm4a9cunQUATJ8wYYJcAsgpyMu6urqww4fA4KysLHWlRvF5D6kB9qL42tvbpcD4yVr7/X4pBdZz27ZtarzC3FLa/PHHH0vhETLMycnRvmKu2FONjY3/1SGofO+AAQN0z6wt146JiZFyRCHExsYqNI1CRCFMmTJF+RwouJUrV8rlYE35ntOnT6tpC9e84447lBHK3KMQm5qaOh04/EXiXAMnTpx0LyIg9BMdHS1EgHYE/lB5Zhbsejxx4kRpRzQzFmTHjh32/PPPm1mgEsssUFdAFh6nxkC+3X///UIES5cuNbNAUhNaGHcEV6WkpCTsslVcg8GDB8uycw2yzAoLCxXqA7YC65kjsyAJePjwYY2F+3/ooYd0xDuWCARx5513CkEsWLDAzDofjIp1WrRokZkFrFq4JwABXbl/syDqoyHGtm3bRNRSOxIfH69QHy4CocLi4mL79a9/bWbBTMSXX35Z9QS4hljl6dOna45+/OMfm1mgszFIEIKauY2IiBCaCEe81aOMkT1Jg9n33ntPyUPM8z333CPExz2T2bh7924dTwZqe/rpp7WPcZH4/Jw5c4RcIZHfeOMNuZHcFzU0tbW12g83EocInDhx0r2IAH+lpKREee2EDfHvMjIyZE0gPVavXq3fqWBDG8fExMgfI6HowoULIsGwnmjLU6dO6buwDFlZWbLYhBsJ2XjPAghVsB4XL16UJeIe+e709PQuCTxPPPGEyDYsJOiiT58+8uFJkqmoqFDiDlWLcARxcXGabxK0srKylOwE8sI3rays1GuhCvdfUlIiK0SYD8Qxd+5c8TNwCs8884zSyCHV4A8GDRokNETItaioSGQaKeOEG/v16ye0Bymal5cnFELdAolOmzdvFs8UjoC4WlpalA5/fbq73+/v0uiW5qlmQT6Gz5WWlsqac82DBw8KLYMmvWQ1iBUEkZiYqP1JhSWE8NWrV0PmfbpVEbDYAwcO1AZmU/NaUlKSJo/c+oEDB+oBBVoDx9asWaM4PD+Tk5MFnVh04vfew0eZpHHjxsnVoIMyBE12draIqlCFh7m5uVnsPJmCfE9SUpK9/vrrZhZke++66y7lSQCBYYkfeeQRKUEy6VJTUwWxITSBxEuWLNGhITyIt9xyi37nGDTcqrvvvlulz6GKt3kKrD5NQpjbYcOGidjiAZ83b56Yc+LoKMxVq1bpGtRlxMbG2o9+9CMzC0Y5eHBmzZol5cf9REVFaQ1g1FEcM2bMUB1EOOIlGCFGgfgoiczMTBG03uPa2LuQnxiZgwcPymh4z9Hg/tgXRIRmzJih6AX3M2nSJBkxSNbf/OY3ZhaIUDGXNxLnGjhx4qR7EQGQ//Dhw11abGHtkpKSpM2BtMOHD5fFozEFMOib3/ymCBSImqSkJIXVsDxkc9XV1QkRYD3/+c9/yvXgNcJfNTU1CkWBFm4kWIiioiJZYK5P3LiysrJTDjnjBT5jRSktffjhh5X1iFtUUFAga0HvP6z673//e80pViQ/P1+hLuYMdHH+/HkdiOolLb9IiKcfP35c8BVXD2geGxurMbHm3nlhramQXLJkiaA275kwYYI+yz7AIh48eFBIA/ertLRUrgdrTKi2d+/eIlHDQQbkcBw+fLjL6UF8f1VVldYSJNrQ0KB9D2pirh5++GEhRhDoiBEjtCbscdyusrIyhX3Jkzlw4ICuS8k9e+Ly5cuqRgVB/ydxiMCJEyfdiwjw71JSUkQS4ddjdePi4tSNFkSQnp4ukgRfCl/70qVLOt+O97zyyivyrQnjwElMnTpVxKH3FCS0MMgBS9XQ0BA2ucQ4R4wYIWuBVocDmDx5slALvQ8SEhKUX49Fw8odP35cVYRY2DVr1tjLL79sZkHSDV5g2bJlslxkVW7atEnjYt4JAdbX14d9XDhkZFRUlCwmiAQk09DQIIIPKzZs2DCbNWtWp7GQDHPgwAFl6pEYs379eoXUIASp08jLy1NmJuPcu3ev9gfzgpWsr6/vFO4MVUAgaWlpCvvCb9FUNj4+Xt2hGWtWVpb2FgQq+2/Pnj3ijgiVv/jii2qqAvID7c2aNUshSIjUd955R+MmFM9clpWVuZOOnDhxErpEwNZ3hzz++OMdZgHNiFbG58Nvuvnmm2W1CJ19/vnn0oTe04nMAn4Z3AAW1ixoadDe+GknT57U9bnWsWPH5LtjhZiXq1evymo+99xznY+n/Q+yfPnyDrMAi41/zvXgCqZMmSKkgQbft2+f+AWQhLeiDj6FeoUePXrIKjMHfF9DQ4MQAajo3LlzstzMJz50R0eH/NsXXnghpHEuXLiwg8+yVgg+72233SYLSLgRpGcWtOLMz7Vr1zRHhOJiYmLEFxAqAxmWl5drrVnPiooKrTeIyoskme/XXnstpHGama1atUprCptPsg681fTp07ugxxMnTmiNiJCA+k6fPq2QLclDffv21X6DKyHRq7KyskvNTUlJiXgt7oeITXx8vO519erVXzjWbnUN6Pbq8/m6nGcAJIekMwsqgvb2dhWRXH8wSnt7u8I41COkpKTos97j080CDwWLxYSNHTtWi4SC8naJDTVfG+Eh69mzpyA+mw+C7dSpUxo735WYmKjNT7iLh9jn8+k16hEIQ3mvC3E0a9YsuSj8Ly4uTteglgLFU15e3uUY7hsJcLlnz56qoSC3n4164cIFPYQQfnFxcVK8kIS4Ko2NjQoNUheRnZ3dKezqHZO3yQ2uYU5OTidXwCzoSh46dCjsvBCz4F7p06ePHl4gPw9xdXW19g+vtbS0aC6oicFNbGmLE6SFAAAAx0lEQVRpkVLg3I7x48d3yqkxCxqzMWPGdOn1mJGRoX1A6TV7v76+PuQ1da6BEydOutc1cOLEyf+nOETgxIkTpwicOHHiFIETJ07MKQInTpyYUwROnDgxpwicOHFiThE4ceLEnCJw4sSJOUXgxIkTc4rAiRMn5hSBEydOzCkCJ06cmFMETpw4MacInDhxYk4ROHHixJwicOLEiTlF4MSJE3OKwIkTJ+YUgRMnTswpAidOnJhTBE6cODGnCJw4cWJOEThx4sTM/g1HFUlbO5fKPQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 288x288 with 16 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "generate_and_save_images(ae_model, 4, test_images[:16, :, :, :])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Train the model\n",
    "Call the `train()` method defined above to train the autoencoder model.\n",
    "\n",
    "We'll print the resulting images as training progresses. At the beginning of the training, the decoded images look like random noise. As training progresses, the model outputs will look increasingly better. After about 50 epochs, they resemble MNIST digits. This may take about one or two minutes / epoch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#TODO 4.\n",
    "# TODO: Your code goes here."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create a GIF\n",
    "\n",
    "Lastly, we'll create a gif that shows the progression of our produced images through training. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Display a single image using the epoch number\n",
    "def display_image(epoch_no):\n",
    "    return PIL.Image.open('./ae_images/image_at_epoch_{:04d}.png'.format(epoch_no))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "display_image(EPOCHS)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "anim_file = 'autoencoder.gif'\n",
    "\n",
    "with imageio.get_writer(anim_file, mode='I') as writer:\n",
    "    filenames = glob.glob('./ae_images/image*.png')\n",
    "    filenames = sorted(filenames)\n",
    "    last = -1\n",
    "    for i,filename in enumerate(filenames):\n",
    "        frame = 2*(i**0.5)\n",
    "        if round(frame) > round(last):\n",
    "            last = frame\n",
    "        else:\n",
    "            continue\n",
    "        image = imageio.imread(filename)\n",
    "        writer.append_data(image)\n",
    "    image = imageio.imread(filename)\n",
    "    writer.append_data(image)\n",
    "\n",
    "import IPython\n",
    "if IPython.version_info > (6,2,0,''):\n",
    "    display.Image(filename=anim_file)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright 2020 Google Inc. Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
